打字稿通用类型参数:T vs T extends {}

Typescript generic type parameters: T vs T extends {}

下面的两个泛型类型参数在功能上有什么区别吗?

function funcA<T>() { }
function funcB<T extends {}>() {}

我看到它们都被使用过,但对它们的区别感到困惑?

是的。在 funcB 中,T 必须扩展 {},这几乎意味着除了 之外的任何东西。 T 可以是原始的。

下面是泛型参数,在下面的例子中用来表示函数可以接受的类型参数:

function funcA<T>(t: T) { }

你可以用它来表示函数的类型 returns 如下:

function funcA<T>(t:T):T {}

另一方面,T extends Something表示该函数可以接受扩展Something的任何参数。假设我们有一个接口 Person 和其他一些实现 Person 的接口,例如 TeacherStudent 我们可以编写一个函数:

function funcA<T extends Person>(t: T)

我们只能使用扩展 T 的参数调用 funcA

注意:我假设您使用的是 TypeScript 3.5 或更高版本;在 TypeScript 3.5 中,generic type parameters are implicitly constrained by unknown instead of the empty object type {} 发生了变化,funcA()funcB() 之间的一些细节也发生了变化。我不想通过谈论 TS3.4 及更低版本中的情况来延长 post 甚至更长的时间。


如果您没有明确地 constrain a generic type parameter via extends XXX, then it will implicitly be constrained by unknown,则所有类型都可分配给 "top type"。所以在实践中,这意味着 funcA<T>() 中的 T 绝对可以是您想要的任何类型。

另一方面,空对象类型{}几乎所有类型都可赋值的类型, 除了 对于 nullundefined,当你启用了 --strictNullChecks compiler option(你应该)。甚至像 stringnumber 这样的原始类型也可以分配给 {}.

所以比较:

function funcA<T>() { }
funcA<undefined>(); // okay
funcA<null>(); // okay
funcA<string>(); // okay
funcA<{ a: string }>(); // okay

function funcB<T extends {}>() { }
funcB<undefined>(); // error
funcB<null>(); // error
funcB<string>(); // okay
funcB<{ a: string }>(); // okay

唯一的区别是T extends {}禁止nullundefined


{}(一种所谓的 "object" 类型)可能会接受像 stringnumber 这样的原语,这可能有点令人困惑。这有助于将 {}{a: string} 等花括号包围的类型以及所有 interface 类型视为不一定是 "true" 对象类型,而是值类型您可以在其中对它们进行索引,就好像它们是对象一样而不会出现运行时错误。除了 nullundefined 之外的基元是 "object-like" 因为你可以把它们当作 wrapped with their object equivalents:

const s: string = "";
s.toUpperCase(); // okay

因此,即使像 string 这样的基元也可以分配给花括号包围的类型,只要这些类型的成员匹配:

const x: { length: number } = s; // okay

如果你真的需要表达一个只接受"true"的类型,即非原始对象,你可以使用object:

const y: object & { length: number } = s; // error
const z: object & { length: number } = { length: 10 }; // okay

但我(认真地)跑题了。


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

Playground link to code