谁能给我解释一下<T>是什么意思?
那是打字稿Generics 声明。
摘录:
软件工程的一个主要部分是构建组件,这些组件不仅具有定义明确且一致的 API,而且还可以重用。能够处理当今数据和未来数据的组件将为您提供构建大型软件系统的最灵活功能。
在 C# 和 Java 等语言中,工具箱中用于创建可重用组件的主要工具之一是泛型,也就是说,能够创建一个可以在多种类型而不是单一类型上工作的组件。这允许用户使用这些组件并使用他们自己的类型。
你提到:
我不知道“T”是什么。
'T' 将是在运行时而不是 compile 时声明的类型。 T 变量可以是任何未声明的变量(我找不到参考,但我会假设任何可用于变量名称的有效字符集)。同样在c# 中,如果T 表示的类型不是值类型而是更复杂的类型(类)或接口,则可以将其命名/声明为TVehicle 或TAnimal 以帮助表示一个有效类型未来的程序员(并且可以被认为是最佳实践,因为 T 并不直观)。我更喜欢TSomething,因为我知道大写的 T 表示泛型。 WSometing 或 ASomething 也是有效的,但我只是不喜欢它。 (例如,Microsoft 的 API 几乎总是 TContext 或 TEntity)。
如果有人可以向我解释这个函数在做什么,那也会很有帮助。
这个函数没有做任何事情。这更像是声明一种可以具有多个运行时类型值的函数。我将直接从上面的链接中摘录一段,而不是解释这一点。
function identity<T>(arg: T): T {
return arg;
}
可以这样使用:
// type of output will be 'string'
let output = identity<string>("myString");
或
// type of output will be 'string', the compiler will figure out `T`
// based on the value passed in
let output = identity("myString");
或
// type of output will be 'number'
let output = identity(8675309);
这可能会引发问题:
为什么要使用泛型
Javascript 有数组,但是当你从数组中检索一个值时,它实际上可以是任何东西(打字稿:any)。使用 typescript,您可以通过以下方式声明它们来获得类型安全:
// Array<T>
let list: number[] = [1, 2, 3];
// or
let list: Array<number> = [1, 2, 3];
现在数组中的每个值都有一个类型。如果您尝试将字符串放入此数组,Typescript 将引发编译时错误。当您检索一个值时,您会获得类型安全和智能感知(取决于您的编辑器):
class Person {
FirstName: string;
}
let people: Array<Person> = [];
people.push({ FirstName: "John" } as Person);
let john = people.pop();
// john is of type Person, the typescript compiler knows this
// because we've declared the people variable as an array of Person
console.log(john.FirstName);
声明类型化的通用约束。 Open - Closed Principle 的一个很好的例子。
在面向对象编程中,开闭原则指出“软件实体(类、模块、函数等)应该对扩展开放,对修改关闭”;[1] 即这样的实体可以允许在不修改其源代码的情况下扩展其行为。
在以下示例中,任何人都可以扩展 Human 或 Cheetah,甚至创建自己的派生类型,并且 Logger 功能将继续工作而无需任何修改。
interface IAnimal {
LegCount: number;
}
class Cheetah
implements IAnimal {
LegCount: number = 4;
}
class Human
implements IAnimal {
LegCount: number = 2;
}
public class Logger<TAnimal extends IAnimal> {
public Log(animal: TAnimal) {
console.log(animal.LegCount);
}
}
var logger = new Logger();
var human = new Human();
logger.Log(human);
Working Example
在前面的示例中,我使用Generic Constraint 将TAnimal 类型限制为程序员可以用来创建Logger 实例的类型,这些类型派生自接口IAnimal。这允许编译器验证 Logger 类是否始终假定该类型具有属性 LegCount。
您可以向我解释为什么他们在 Typescript 文档中放置 而不是放置更具描述性的内容,例如 。对我来说没有什么和 是一样的。现在是不是每个人都像傻瓜一样使用 ,还是我错过了什么?
这些都是下面的假设。我不知道设计打字稿通用系统的团队和编写文档的团队。
在泛型的根级别是能够将T 用作任何可能的类型(不要与打字稿any 混淆)。意思是Array<T> 是接口(因为没有更好的词),当我们创建一个具体类型时,我们用声明的类型替换T:
Array<number>
那么对于 界面 Array<T> 有什么比 T 更有意义?我不知道。我知道T 必须是一个类型(数字、字符串等),所以使用T 是有意义的,因为它是单词Type 的第一个字母。我认为如果 type 或 Type 被保留或限制,Array<Type> 将非常令人困惑和/或什至可能无效(目前type 在某些情况下具有特殊含义,所以这也是一个糟糕的选择)所以避免这些是一个不错的选择。其他语言(C-sharp、Java)也选择使用T,因此在语言之间切换并能够使用相同的术语是有利的。
另一方面,以下是什么意思?
Array<Identity>
这里的Identity 是什么?没有任何限制可以帮助其他开发人员或未来的开发人员知道它是什么。在我看来,它是一个我必须显式实现的特定类型数组,这意味着我不能选择泛型类型。
interface Foo1 {
bars: Array<Identity>;
}
在前面的示例中,我(可能还有大多数开发人员)会假设 Identity 是一个现有类型,我无法更改它。
interface Foo2<T> {
bars: Array<T>;
}
有了Foo2,我知道我必须选择一种类型。
interface Foo3<Identity> {
bars: Array<Identity>;
}
Foo3 只是令人困惑。
interface Foo4<TIdentity> {
bars: Array<TIdentity>;
}
现在有了Foo4,我更有信心必须选择类型,但我仍然有点困惑为什么TIdentity。显然,在某些情况下,类型更明确,这是有道理的。
编辑:
自 2021 年 3 月起,Typescript Documentation 已更新为在文档中弃用 <T>,取而代之的是 <Type>。
既然如此,我个人发现以下内容令人困惑,并强烈建议避免使用Type,原因有两个。
interface Lengthwise {
length: number;
}
function loggingIdentity<Type extends Lengthwise>(arg: Type): Type {
console.log(arg.length);
return arg;
}
在使用约束时避免将类型命名为Type 的第一个原因是它在方法中和多个方法中都不能很好地读取。想象一下,日志记录方法比 2 行长得多,突然间,你读到的所有东西都是 Type 这个词。
interface Lengthwise {
length: number;
}
interface Widthwise {
width: number;
}
function loggingLength<Type extends Lengthwise>(arg: Type): Type {
console.log(arg.length);
return arg;
}
function loggingWidth<Type extends Widthwise >(arg: Type): Type {
console.log(arg.width);
return arg;
}
我认为以下内容更具可读性:
interface Lengthwise {
length: number;
}
interface Widthwise {
width: number;
}
function loggingLength<TLengthwise extends Lengthwise>(arg: TLengthwise): TLengthwise {
console.log(arg.length);
return arg;
}
function loggingWidth<TWidthwise extends Lengthwise>(arg: TWidthwise ): TWidthwise {
console.log(arg.length);
return arg;
}
其次,很可能有 1 个以上的泛型,这使得以下内容无效(希望是出于显而易见的原因)。
interface Lengthwise {
length: number;
}
interface Widthwise {
width: number;
}
function loggingLength<Type extends Lengthwise, Type extends Widthwise>
(arg1: Type, arg2: Type) {
console.log(arg1.length);
console.log(arg2.width);
}
虽然文档使用Type 来展示简单和平凡的示例,但我强烈建议不要在任何实际代码中使用Type。