Что в Typescript означает <T>?

export declare function createEntityAdapter<T>(options?: {
    selectId?: IdSelector<T>;
    sortComparer?: false | Comparer<T>;
}): EntityAdapter<T>;

Может кто-нибудь объяснить мне, что значит? Я знаю, что <> является утверждением типа, но я не знаю, что такое 'T'. Было бы также полезно, если бы кто-нибудь мог объяснить мне, что делает эта функция.

Ответ 1

  Может кто-нибудь объяснить мне, что <T> означает?

Это обобщение декларации.

Выдержка:

Основная часть программной инженерии - это создание компонентов, которые не только имеют четко определенные и согласованные API-интерфейсы, но и могут использоваться повторно. Компоненты, способные работать как с данными сегодняшнего дня, так и с данными завтрашнего дня, предоставят вам самые гибкие возможности для построения больших программных систем.

В таких языках, как С# и Java, одним из основных инструментов в наборе инструментов для создания повторно используемых компонентов является обобщение, то есть возможность создавать компонент, который может работать с различными типами, а не с одним. Это позволяет пользователям использовать эти компоненты и использовать их собственные типы.

.

Я не знаю, что такое "Т".

'T' будет типом, объявленным во время выполнения, а не во время компиляции. Переменная T может быть любой необъявленной переменной (я не могу найти ссылку, но я бы предположил любой допустимый набор символов, который можно использовать для имен переменных). Аналогично, в , если представленный тип T является не типом значения, а более сложным типом (классом) или интерфейсом, его можно назвать /delcared как TVehicle или TAnimal, чтобы помочь обозначить допустимый тип для будущие программисты (и это можно считать лучшей практикой, потому что просто T не интуитивно понятен). Я предпочитаю TSomething, потому что я знаю, что верхний регистр T означает универсальный тип. WSometing или ASomething также допустимы, но я просто не предпочитаю это. (API-интерфейсы Microsoft почти всегда представляют собой, например, 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). С машинописью вы получаете безопасность типов, объявляя их как:

 // Array<T>
 let list: number[] = [1, 2, 3];
 // or 
 let list: Array<number> = [1, 2, 3];

Теперь каждое значение в массиве имеет тип. Typescript выдаст ошибку времени компиляции, если вы попытаетесь поместить строку в этот массив. И вы получаете безопасность типов и intellisense (в зависимости от вашего редактора), когда вы получаете значение:

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);  

Объявление типовых общих ограничений. Очень хороший пример открытого - закрытого принципа.

В объектно-ориентированном программировании принцип открытого/закрытого состояния гласит: "программные сущности (классы, модули, функции и т.д.) Должны быть открыты для расширения, но закрыты для модификации"; [1] то есть такая сущность может допускать свое поведение быть расширенным без изменения его исходного кода.

В следующем примере любой может расширить Human или Cheeah или даже создать свой собственный производный тип, и функциональность 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);      

Рабочий пример

В предыдущем примере я использовал Generic Constraint, чтобы ограничить число типов, которые могут использовать программисты TAnimal для создания экземпляра Logger, типами, производными от интерфейса IAnimal. Это позволяет компилятору проверить, что класс Logger всегда предполагает, что тип имеет свойство LegCount.

Ответ 2

Пример, который вы предоставляете, - это функция с общим параметром. T (который не должен быть T. Вы можете назвать его G.) называется общим шаблоном, где фактический тип T заменяется во время выполнения.

Представьте, что EntityAdapter имеет следующую реализацию:

interface EntityAdapter<T> {
   save(entity: T);
}

Ниже код возвращает EntityAdapter, содержимое которого any. any может быть числом, строкой, объектом или чем-либо.

let adapter1 = createEntityAdapter<any>(<parameters here>)

Ниже код возвращает EntityAdapter, содержимое которого Car.

let adapter2 = createEntityAdapter<Car>(<parameters here>)

В основном Car более конкретный, чем any, чтобы вы могли получить дополнительную безопасность типа. Так как это помогает?

Вкратце, общий шаблон может дать вам дополнительную безопасность типов. Например,

adapter1.save('I am string') // this works because `T` is `any`
adapter1.save(new Car()) // this also works because `T` is `any`

adapter2.save('I am string') // this wont works  because `T` is `Car`, typescript compiler will complains
adapter2.save(new Car()) //this works because `T` is `Car`

Надеюсь, что это поможет.