Typescript通用类型参数:T vs T extends {} Typescript中的泛型类型参数有两种常见的形式:T和T extends {}。

37
下面的两个通用类型参数之间在功能上是否有任何区别?
function funcA<T>() { }
function funcB<T extends {}>() {}

我看到这两个术语都被使用过,但是它们的区别让我感到困惑?

3个回答

45
注意:我假设你正在使用 TypeScript 3.5 或更高版本;在 TypeScript 3.5 中,有一个改变,即泛型类型参数隐式受到 unknown 的约束,而不是空对象类型 {}, 并且关于 funcA()funcB() 之间的差异的一些细节也发生了变化。我不想通过谈论 TS3.4 及以下版本的情况来使一篇长帖子变得更长。
如果你没有通过extends XXX显式地约束泛型类型参数,那么它将隐式地受到unknown的限制,这是所有类型都可以赋值的“顶级类型”。因此,在实践中,这意味着funcA<T>()中的T可以是任何类型。
另一方面,空对象类型{}是一个类型,几乎所有类型都可以分配给它,除了nullundefined,当你启用了--strictNullChecks编译器选项(应该这样做)时。甚至像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
这可能有点令人困惑,被称为“对象”类型的{}可以接受像stringnumber这样的基本类型。将这些花括号包围的类型,如{}{a: string}以及所有interface类型,不一定视为“真正”的对象类型,而是作为值的类型,您可以像访问对象一样对它们进行索引,而不会出现运行时错误。除了nullundefined之外的原始类型都类似于“对象”,因为您可以将它们视为使用其对象等效物进行封装
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

但我(严肃地)离题了。

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

代码游乐场链接


6

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


0
以下是指示一个通用参数,在下面的情况中用于指示函数可以接受的类型参数:
function funcA<T>(t: T) { }

您可以使用以下方式指示函数返回的类型:

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

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

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


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接