В чем разница между общим синтаксисом функций:
type Identity<T> = (t: T) => T
и
type Identity = <T>(t: T) => T
?
В TypeScript есть два различных варианта дженерики: универсальный функции и универсальный типы.
Универсальная функция объявляет свой параметр (или параметры) универсального типа в сигнатуре вызова функции:
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 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 представит выражения инстанцирования, который позволит вам создавать экземпляры параметров типа для ценность, тип которого является универсальной функцией:
// 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+
Ссылка на код для игровой площадки