泛型参数放置之间的打字稿差异
typescript difference between placement of generics arguments
泛型函数语法有什么区别:
type Identity<T> = (t: T) => T
和
type Identity = <T>(t: T) => T
?
TypeScript 中有两种不同风格的 generics:泛型 functions 和泛型 types.
泛型函数在函数的调用签名上声明其泛型类型参数(或多个参数):
type IdentityGenFunc = <T>(t: T) => T
泛型函数(上面的 T
)的类型参数直到实际调用函数时才会指定,此时调用者指定它(或编译器代表调用者推断它) .这意味着通用函数实现必须能够处理函数调用者想要的任何可能的 T
规范:
const idGenFunc: IdentityGenFunc = x => x;
const resultABC = idGenFunc("ABC"); // T inferred here as ABC
// const resultABC: "ABC"
const result123 = idGenFunc(123); // T inferred here as 123
// const result123: 123
泛型 type 在类型声明中声明其泛型类型参数(或多个参数):
type IdentityGenType<T> = (t: T) => T
泛型类型(T
以上)的类型参数必须先指定,然后您才能拥有该类型的值。如果泛型类型具有引用类型参数的调用签名或方法,则在调用该方法之前,只要指定了周围的泛型类型,该类型参数就会固定。一旦您谈论 IdentityGenType<string>
,那么它的调用签名只需要能够处理 string
。因此,要实现该功能,您不需要处理 T
:
的任何可能值
const idGenTypeString: IdentityGenType<string> = x => x + "!";
const resultABCagain = idGenTypeString("ABC"); // okay, "ABC" is assignable to string
// const resultABCagain: string
const result123Again = idGenTypeString(123); // error! 123 is not assignable to string
这两种类型的泛型是相互关联的。类型 IdentityGenFunc
本质上是 IdentityGenType<T>
对每个可能的 T
的“无限交集”。 TypeScript 类型系统的表现力不足以说明这一点:
// the following is not valid TS, but this is what you want to say
type IdentityGenFunc = forall T, IdentityGenType<T>; // not valid TS, error
但你可以通过作业见证它:
// every IdGenFunc is also an IdGenType<number>
const idGenTypeNumber: IdentityGenType<number> = idGenFunc; // okay
另一方面,您可以认为 IdentityGenType<T>
与 IdentityGenFunc
实例化 具有某种类型 T
相同。同样,类型系统不允许您这样表达:
// the following is not valid TS, but this is what you want to say
type GenType<T> = instantiate GenFunc with T; // not TS, error
尽管 TypeScript 4.7 将引入 instantiation expressions,这将使您可以在类型为通用函数的 value 上实例化类型参数:
// if you instantiate a value of type IdGenFunc with Date, then you get an IdGenType<Date>
const idGenTypeDate: IdentityGenType<Date> = idGenFunc<Date>; // okay for TS4.7+
泛型函数语法有什么区别:
type Identity<T> = (t: T) => T
和
type Identity = <T>(t: T) => T
?
TypeScript 中有两种不同风格的 generics:泛型 functions 和泛型 types.
泛型函数在函数的调用签名上声明其泛型类型参数(或多个参数):
type IdentityGenFunc = <T>(t: T) => T
泛型函数(上面的 T
)的类型参数直到实际调用函数时才会指定,此时调用者指定它(或编译器代表调用者推断它) .这意味着通用函数实现必须能够处理函数调用者想要的任何可能的 T
规范:
const idGenFunc: IdentityGenFunc = x => x;
const resultABC = idGenFunc("ABC"); // T inferred here as ABC
// const resultABC: "ABC"
const result123 = idGenFunc(123); // T inferred here as 123
// const result123: 123
泛型 type 在类型声明中声明其泛型类型参数(或多个参数):
type IdentityGenType<T> = (t: T) => T
泛型类型(T
以上)的类型参数必须先指定,然后您才能拥有该类型的值。如果泛型类型具有引用类型参数的调用签名或方法,则在调用该方法之前,只要指定了周围的泛型类型,该类型参数就会固定。一旦您谈论 IdentityGenType<string>
,那么它的调用签名只需要能够处理 string
。因此,要实现该功能,您不需要处理 T
:
const idGenTypeString: IdentityGenType<string> = x => x + "!";
const resultABCagain = idGenTypeString("ABC"); // okay, "ABC" is assignable to string
// const resultABCagain: string
const result123Again = idGenTypeString(123); // error! 123 is not assignable to string
这两种类型的泛型是相互关联的。类型 IdentityGenFunc
本质上是 IdentityGenType<T>
对每个可能的 T
的“无限交集”。 TypeScript 类型系统的表现力不足以说明这一点:
// the following is not valid TS, but this is what you want to say
type IdentityGenFunc = forall T, IdentityGenType<T>; // not valid TS, error
但你可以通过作业见证它:
// every IdGenFunc is also an IdGenType<number>
const idGenTypeNumber: IdentityGenType<number> = idGenFunc; // okay
另一方面,您可以认为 IdentityGenType<T>
与 IdentityGenFunc
实例化 具有某种类型 T
相同。同样,类型系统不允许您这样表达:
// the following is not valid TS, but this is what you want to say
type GenType<T> = instantiate GenFunc with T; // not TS, error
尽管 TypeScript 4.7 将引入 instantiation expressions,这将使您可以在类型为通用函数的 value 上实例化类型参数:
// if you instantiate a value of type IdGenFunc with Date, then you get an IdGenType<Date>
const idGenTypeDate: IdentityGenType<Date> = idGenFunc<Date>; // okay for TS4.7+