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

时间:2020-05-07 01:15:33

标签: typescript

下面的两个泛型类型参数在功能上是否有区别?

sum()

我看到它们都被使用了并且对它们的区别感到困惑吗?

3 个答案:

答案 0 :(得分:3)

注意:我假设您使用的是TypeScript 3.5或更高版本。在TypeScript 3.5中进行了更改,因此generic type parameters are implicitly constrained by unknown instead of the empty object type {}以及有关funcA()funcB()之间的区别的一些次要详细信息已更改。我不想通过谈论TS3.4及以下版本中的情况来写更长的篇幅。


如果没有通过extends XXX显式地constrain泛型类型参数,则它将隐式地受unknown约束,{所有类型都可以分配给该类型的“顶部类型”。因此,实际上,这意味着T中的funcA<T>()可以是您想要的任何类型。

另一方面,空对象类型{}是所有类型都可以分配给 的类型,{{ 1}}和null (应启用--strictNullChecks compiler option)。甚至undefinedstring之类的原始类型也可以分配给number

所以比较:

{}

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 {}null


undefined(所谓的“对象”类型)接受诸如{}string之类的原语可能会有点令人困惑。有助于将诸如number{}之类的花括号包围的类型以及所有{a: string}类型不一定视为“ true”对象类型,而是将其视为值类型可以像它们是对象一样索引它们,而不会出现运行时错误。 interfacenull以外的基元是“类对象”,因为您可以将它们视为wrapped with their object equivalents

undefined

因此,即使const s: string = ""; s.toUpperCase(); // okay 之类的基元也可以分配给花括号包围的类型,只要这些类型的成员匹配:

string

如果您确实需要表达仅接受“ true”的类型,即非原始对象,则可以使用object

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

但是我(严重)离题了。


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

Playground link to code

答案 1 :(得分:1)

是的。在funcB中,T必须扩展{},这几乎意味着除null and undefined以外的任何内容。 T可以是原始的。

答案 2 :(得分:0)

以下是指示通用参数,在以下情况下,用于指示函数可以接受的类型参数:

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

您可以使用它来指示函数返回的类型,如下所示:

function funcA<T>() T {}

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

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

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