Ответ 1
Может кто-нибудь объяснить мне, что
<T>
означает?
Это обобщение декларации.
Выдержка:
Основная часть программной инженерии - это создание компонентов, которые не только имеют четко определенные и согласованные API-интерфейсы, но и могут использоваться повторно. Компоненты, способные работать как с данными сегодняшнего дня, так и с данными завтрашнего дня, предоставят вам самые гибкие возможности для построения больших программных систем.
В таких языках, как С# и Java, одним из основных инструментов в наборе инструментов для создания повторно используемых компонентов является обобщение, то есть возможность создавать компонент, который может работать с различными типами, а не с одним. Это позволяет пользователям использовать эти компоненты и использовать их собственные типы.
.
Я не знаю, что такое "Т".
'T'
будет типом, объявленным во время выполнения, а не во время компиляции. Переменная T
может быть любой необъявленной переменной (я не могу найти ссылку, но я бы предположил любой допустимый набор символов, который можно использовать для имен переменных). Аналогично, в c#, если представленный тип 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
.