hyltonw
hyltonw

Reputation: 13

Is there a way to have a generic type use the type of one of it's properties?

If I have a generic type like the following

type Foo<T> = {
    propertyA: T[],
    propertyB: number,
    functionA: (a: T) => void
}

is there a way to infer the type of T based on whatever the type propertyA is set to, instead of having to explicitly declare it whenever I'm creating a Foo object?

let fooArray: string[] = ['one','two','three']

// current way
let newFoo: Foo<string> = {
    propertyA: fooArray,
    propertyB: 10,
    functionA: (a) => {
        console.log(a)
    } 
}

// Desired way
let newFooToo: Foo = {
    propertyA: fooArray,
    propertyB: 10,
    functionA: (a) => {
        console.log(a) // Intellisense available for strings in this example
    } 
}

I know this is kind of going the opposite way of how generics are used, so it doesn't have to be a generic type implementation if it's not possible.

Upvotes: 1

Views: 30

Answers (2)

Etienne Laurin
Etienne Laurin

Reputation: 7184

Passing the Foo object to an identity function allows stricter type checking, for example:

function mkFoo<T>(foo: Foo<T>){ return foo }

Using it will infer the parameter a as a string:

let newFoo = mkFoo({
    propertyA: fooArray,
    propertyB: 10,
    functionA: (a) => {
        console.log(a) // Intellisense available for strings
    } 
})

Upvotes: 0

Jos&#233; Ram&#237;rez
Jos&#233; Ram&#237;rez

Reputation: 2360

The type works as-is. What you probably want is Intellisense to work for you. Unfortunately, you need to tell Intellisense that you are writing a Foo<> object. Yes, typing it as you declare it is a way, but another way is to use satisfies.

See this playground.

You can see that TypeScript can use your type as-is if you declare a function that uses it. As seen in the following playground screenshot, after I have typed propertyA, Intellisense knows that T === string:

Playground example

See how functionA's parameter is predicted as of type string? There's nothing to do.

My Recommendation

I would start typing the variable like this:

const x = {} satisfies Foo<Date>;

At this point, you have given TypeScript/Intellisense the necessary information to give you property prediction, so you can finish typing it.

Why Your Expectation Cannot Be Met

This is the desired result as per your question:

// Desired way
let newFooToo: Foo = {
    propertyA: fooArray,
    propertyB: 10,
    functionA: (a) => {
        console.log(a) // Intellisense available for strings in this example
    } 
}

This cannot possibly happen with current TypeScript. The type inference mechanism doesn't cover this kind of syntax (where the type parameter is omitted). Maybe in the future? Right now, this is not a thing.

Upvotes: 0

Related Questions