简体   繁体   中英

Generic type of extended interface not inferred

In the following example Typescript can infer the type of T in method foo from the parameter passed to it in bar , but it doesn't infer the type of R , which it feels like it should - given that it knows the type of T and also that T extends I<R>

interface I<T> {
}

class A implements I<string> {

}

function foo<T extends I<R>, R>(bar: T): R {
    return;
}

foo(new A());

Is there another way to do this?

The first problem is that your interface is empty, typescript uses structural typing so if your generic interface will not use it's type parameter it won't matter much that it has it. For example this works:

interface I<T> { }
declare let foo: I<string> 
declare let bar: I<number>
// Same structure ({}), assignment works
foo = bar
bar = foo 

Event if we add a field, Typescript will still not infer the R type parameter, it just doesn't try to extract it from T . Your best option is to use a conditional type and extract the generic type parameter where you need it:

interface I<T> {
    value :T 
}

class A implements I<string> {
    value! :string 
}

type ExtractFromI<T extends I<unknown>> = T extends I<infer U> ? U : never;
function foo<T extends I<unknown>>(bar: T):  ExtractFromI<T>{
    return bar.value as ExtractFromI<T>; // Generic type with unresolved type parameters, we need a type assertion to convince the compiler this is ok 
}
var r = foo(new A()); // string

You may also just want to extract it from the implementation.

eg.

export class CatWatcher extends Watcher<Cat>
{
    watch(): Cat { ... }  // overridden from base
}

I want to get Cat from CatWatcher . As other answers have said it doesn't seem possible to extract it from the base class with inference.

But you know it will have a watch function - so extract it from there.

export type ExtractWatcherType<T> = T extends { watch: infer W } ? W : never;

I'd like to add on the accepted answer from @Titan. You could also add a hidden Symbol on the interface to trick the structural typing.

I used the following in my project and the compiler is able to infer the Result type of my Action. You can try it in the Typescript Playground .

const F = Symbol();

interface Action<R = unknown> {
    [F]?: R; // this trick the compiler
}

type ActionResult<T extends Action> = T extends Action<infer R> ? R : never;

interface SetName extends Action<SetNameResult> {
    type: 'set_name';
    name: string;
}

interface SetNameResult {
    type: 'set_name_result';
    newName: string;
}

function process<A extends Action>(action: A): ActionResult<A> {
    return {} as any; // TODO process action and return a valid result
}

const r = process({ type: 'set_name', name: 'the name' } as SetName);
r.type; // 'set_name_result'
r.newName; // string

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM