Typescript泛型:从函数参数的类型推断类型?

时间:2019-11-26 16:16:06

标签: typescript typescript-generics

我有一个带有2个参数的方法,我希望它从第一个参数推断类型。

例如,在下面的代码中,我希望从T推断出函数create_C<T>的类型firstArgument,以便create_C函数的返回类型将为C<type inferred from firstArgument>

interface C<T> { 
    firstArgument: A<T>;
    secondArgument: (obj: any) => T
}

export interface A<T> {
    type: T;
}

function create_C<T>(
    firstArgument: A<T>,
    secondArgument: (obj: any) => T
): C<T> {
    return {
        firstArgument,
        secondArgument
    }
}

但是,在以下实现中,const c的类型被推断为C<{ prop2: number }>。但是我希望将其推断为C<B>,并且希望编译器抛出错误,指出secondArgument的返回类型不是B

类型
interface B { 
    prop1: string;
    prop2: number
}

export class B_Component implements A<B> {
    type: B = {
        prop1: "",
        prop2: 1
    };
}

const c = create_C(
    new B_Component(),
    () => ({ prop2: 2 })
)

如何确保编译器抛出错误,说明secondArgument的返回类型不是B类型?

这是Stackblitz编辑器链接:https://stackblitz.com/edit/qqddsn

2 个答案:

答案 0 :(得分:3)

在您的功能签名中

declare function create_C<T>(a1: A<T>, a2: (obj: any) => T): C<T>;

T有两个推断站点(“推断站点”是指“某个地方编译器可用来尝试为类型参数推断类型”)。一个站点来自第一个参数type的{​​{1}}属性,另一个站点来自第二个参数a1的返回类型。编译器会像这样调用

a2

并尝试从两个站点推断create_C(new B_Component(), () => ({ prop2: 2 }); 。在这种情况下,存在一个匹配项:T(new B_Component()).type都可以分配给{prop2: 2}。这样就没有错误,并且您得到{prop2: number}的提示。在另一种情况下,这可能正是您希望编译器提供的行为。


相反,您希望看到编译器仅使用C<{prop2: number>来推断a1,并仅验证T与之匹配。也就是说,您希望a2中的Tnon-inferential type parameter (see microsoft/TypeScript#14829)。不幸的是,对此没有“官方”支持。但是幸运的是,有一些变通方法通常可以用来获得此行为。

这是一种这样的技术:如果将推理站点中的类型参数从(obj: any) => T更改为T,则将其更改为lowers the site's priority。因此,编译器将倾向于首先从其他推断站点推断T & {},并且只有在无法从其他地方推断时才返回到T。而且类型T & {}T & {}非常相似(如果T是对象类型,则基本上是相同的),因此它的语义变化不大。试试吧:

T

在这里:

declare function create_C_better<T>(a: A<T>, b: (obj: any) => T & {}): C<T>;

在那里,当缺少const c2 = create_C_better( new B_Component(), () => ({ prop2: 2 }) // error! // ~~~~~~~~~~~~~~ <-- prop1 is missing ) const c3 = create_C_better( new B_Component(), () => ({ prop1: "all right", prop2: 2 }) ); // C<B> 时,您将得到所需的错误,并且在对其进行修复时,将根据需要获得类型为prop1的输出。


好的,希望能有所帮助;祝你好运!

Link to code

答案 1 :(得分:0)

这是由于secondArgument: (obj: any) => T引起的。 如果您将以上定义应用于() => ({ prop2: 2 }) T的类型为{ prop2: number }。您可以将其更改为其他内容以获得所需的结果。 例如。

    interface C<T> {
      firstArgument: A<T>;
      secondArgument: (obj: any) => any;
    }

    export interface A<T> {
      type: T;
    }

    declare function create_C<T>(
      firstArgument: A<T>,
      secondArgument: (obj: any) => any
    ): C<T>;

    interface B {
      prop1: string;
      prop2: number;
    }

    export class B_Component implements A<B> {
      type: B;
      configuration: B = {
        prop1: "",
        prop2: 1
      };
    }
    const b = new B_Component();
    export const c = create_C(b, () => ({ prop2: 2 }));