Разница машинописного текста между размещением аргументов дженериков

В чем разница между общим синтаксисом функций:

type Identity<T> = (t: T) => T

и

type Identity = <T>(t: T) => T

?


3
31
1

Ответ:

Решено

В 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+

Ссылка на код для игровой площадки