打字稿泛型:从函数参数的类型推断类型?

Typescript generics: infer type from the type of function arguments?

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

例如,在下面的代码中,我希望从 firstArgument 推断出函数 create_C<T> 的类型 T,以便 return 类型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 的 return 类型不是 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 的 return 类型不是 B 类型?

这是 Stackblitz 编辑 link:https://stackblitz.com/edit/qqddsn

这是由于 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 }));

在你的函数签名中

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

T 有两个推理位点("inference site" 表示 "someplace the compiler can use to try to infer a type for a type parameter")。一个站点来自第一个参数a1type属性,另一个站点是第二个参数a2的return类型。编译器查看像

这样的调用
create_C(new B_Component(), () => ({ prop2: 2 });

并尝试从两个站点推断 T。在这种情况下,有一个匹配项:(new B_Component()).type{prop2: 2} 都可以分配给 {prop2: number}。所以没有错误,你得到 C<{prop2: number> 出来。在另一种情况下,这可能正是您希望编译器实现的行为。


相反,您希望看到编译器仅使用 a1 来推断 T,并仅验证 a2 是否匹配它。也就是说,您希望 (obj: any) => T 中的 T 成为 non-inferential type parameter (see microsoft/TypeScript#14829)。不幸的是,没有 "official" 对此的支持。但幸运的是,通常可以使用变通技术来获得此行为。

这是一种这样的技术:如果您将推理站点中的类型参数从 T 更改为 T & {},它会 lowers the site's priority。因此,编译器将倾向于先从其他推理站点推断 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 时,您会得到想要的错误,当您修复它时,您会根据需要得到 C<B> 类型的输出。


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

Link to code