Niekvb
Niekvb

Reputation: 143

TypeScript: Infer literal type of already defined type

Not sure if this is possible or to best describe this, but I need to infer the literal type of a type that's already defined by another type/interface.

For example, I've got an object that adheres to a certain interface:

interface MyObject {
    foo: string | number;
    bar: string[];
}
const MY_OBJECT: MyObject = {
    foo: 123,
    bar: ["abc", "def"],
};

Now, what I'd like to know is the type literal of MY_OBJECT, by doing something like this:

type MyObjectLiteral = Infer<typeof MY_OBJECT>;

That should make MyObjectLiteral: { foo: 123, bar: ["abc", "def"] }

The question is how to define type Infer<T> to return the inferred type of T.

Any help or clarification would be appreciated.

Upvotes: 0

Views: 28

Answers (1)

Alex Wayne
Alex Wayne

Reputation: 186994

This isn't really possible with types alone. A soon as you assign a value to a type that is wider than what could be inferred, you lose that type information.

But you can always treat a more narrow type as the wider type that includes it. Which means you can always go the other direction.


You could simply declare your object as const and then use it anywhere that accepts a MyObject type.

interface MyObject {
    foo: string | number;
    bar: readonly string[];
}

const myObject = { foo: 123, bar: ["abc", "def"] } as const
const barZero = myObject.bar[0] // type: "abc"

const myObjectWide: MyObject = myObject // works
const barWideZero = myObjectWide.bar[0] // type: string

Playground


Or if you want to constrain the construction of myObject you can use a function to capture the exact subtype, and return it.

For example:

interface MyObject {
    foo: string | number;
    bar: readonly string[];
}

function makeObj<T extends MyObject>(myObject: T): T {
    return myObject
}

const myBadObject = makeObj({ bad: true } as const)
// Argument of type '{ readonly bad: true; }' is not assignable to parameter of type 'MyObject'.
//  Object literal may only specify known properties, and 'bad' does not exist in type 'MyObject'.(2345)

const myObject = makeObj({ foo: 123, bar: ["abc", "def"] } as const)
const barZero = myObject.bar[0] // type: "abc"

const myObjectWide: MyObject = myObject // works
const barWideZero = myObjectWide.bar[0] // type: string

Playground

Upvotes: 1

Related Questions