首页 > 解决方案 > 将泛型类型传递给打字稿中的函数

问题描述

考虑示例:

interface A {
   foo: string;
}

interface B {
   foo: string;
}

function a<T>() {
   function b(arg: T) {
      return arg.foo;
            ^^^^^^ Property 'foo' does not exist on type 'T'.
   }

   return b;
}

a<A>();
a<B>();

如何使它工作,所以我不必硬核这样的类型:

function b(arg: A | B)

所以我将能够将类型作为泛型传递,所以如果有 5 种甚至更多不同的类型,我就不必像A | B | C | D | E...泛型那样做吗?

标签: javascripttypescript

解决方案


为了让您能够访问arg.foo,编译器必须确信,至少,arg实际上可能有一个名为 的属性foo。所以 的类型arg必须可以分配给类似的东西{foo?: unknown},其中foo是一个可选属性,属性unknown类型是类型

从您的示例中不清楚您是否要制作比这更严格的东西(比如说它肯定有一个名为 的属性foo,或者该属性的值必须是 a string)。现在,我只假设您想要arg.foo无错误地访问。

如果您使用泛型类型参数T作为 的类型arg,则T必须将其限制为可分配给 的东西{foo?: unknown}

例如:

function a<T extends { foo?: unknown }>() {
    function b(arg: T) {
        return arg.foo; // okay, no error
    }    
    return b;
}

T extends A | B | C | ...假设所有的A, B, C, etc... 类型本身都可以分配给{ foo?: unknown }. 您的给定AB类型是可分配的,因此以下工作:

a<A>();
a<B>();

请注意,示例中的您的AB类型实际上是相同的。TypeScript 的类型系统是结构化的。因为类型A结构B相同,所以它们是相同的类型。有两个声明和两个不同的名称并不重要:

let aVal: A = { foo: "a" };
let bVal: B = { foo: "b" };
aVal = bVal; // okay
bVal = aVal; // okay

因此,如果您希望编译器强制执行存在并且是; ,那么编写T extends A会很好。它不会停止工作。结构类型系统使您不必预测所有可能的命名类型:foostringB

function a2<T extends A>() {
    function b(arg: T) {
        return arg.foo.toUpperCase();
    }
    return b;
}
    
a2<A>(); // okay
a2<B>(); // okay

interface C {
    foo: string;
    bar: number;
}
a2<C>(); // okay

a2<Date>(); // error
// ~~~~
// Property 'foo' is missing in type 'Date' but required in type 'A'.

如果您关心 name 的“硬编码” ,您可以使用像inA这样的匿名类型,但从类型系统的角度来看,这并没有太大变化,因为和是相同的类型。{foo: string}T extends {foo: string}A{foo: string}

Playground 代码链接


推荐阅读