1

Is there a way to ensure at compile time that an inferred type matches another interface without losing the inferred type?

Say with have this simple interface and an object which implements it:

interface Person {
  name: string;
}

const bob = {
  name: "Bob"
}

type Bob = typeof bob;

I want to keep the inferred type Bob which is more specific than Person, but I also want to make sure it matches Person.

const bob: Bob = {  // this kills the implicit type
  name: "bob"
}

----

const bob = {}  // but I want this to fail, because `name` is missing

----

const bob = {
  name: "Bob",
  age: 30, // I want this to fail too, because it doesn't correspond to `Person`
}

2 Answers 2

2

I think it's fair to say that the "typescript way" would be to extend the interface.

interface Person {
  name: string;
}

interface Bob extends Person {
  name: "Bob"
}

Admittedly it's not semantically identical, but I think it achieves what you're looking for.

interface Person {
    name: string;
}

interface Bob extends Person {
    name: "Bob"
}

function test()
{
    const bob: Bob = {  // works
        name: "Bob"
    }

    const bob2: Bob = {}  // Not allowed

    const bob3: Bob = {
        name: "Bob",
        age: 30, // Not allowed
    }
}
Sign up to request clarification or add additional context in comments.

Comments

0

Usually this is managed by a consuming function. For example instead of a no-op that just asserts the type you refactor code that consumes bob into functions that accept Person type parameters. If you want them to be "More than a Person" you can have a signature like <T extends Person>(p: T) => void

That being said it can be done, though it will generate real code that doesn't do anything useful.

Here's an example where an asserter function accepts a generic type argument, and produces an object with a .assert method that will pass-through the type of whatever you hand it, while also checking it extends the intended type.

interface Person {
  name: string;
}

function asserter<Intended>() {
    return {
        assert: function<T>(input: T extends Intended ? T : never): T {
            return input as any
        }
    }
}

// the extra "water" will be accepted, but this won't compile if "name" is missing
const bob = asserter<Person>().assert({ name: '', water: ''})

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.