Я обратился к истории и проверил, у этой истории нет хронологии, и на каждой перекошенной странице написаны слова «доброжелательность, праведность и нравственность».
Я не мог спать ни в горизонтальном, ни в вертикальном положении.После внимательного прочтения среди ночи я увидел в трещинах слова.По всей книге были написаны два слова «Ешь людей!».
предисловие
не делай заметки, не учись
онлайнTypeScript
Адрес практики:TypeScript Playground
Зачем нужен TypeScript
TypeScript
даJavaScript
надмножество , так как оно расширяетJavaScript
,имеютJavaScript
Ничего такого. Если вы настаиваете на детско-родительских отношениях,TypeScript
даJavaScript
Подклассы, расширяющиеся на основе наследования.
TypeScript
Первопричиной рождения являетсяJavaScript
Это слаботипизированный язык (тип языка может быть неявно преобразован), и невозможно выполнить проверку типов на этапе компиляции для раннего обнаружения ошибок.
TypeScript
Первоначальное намерение состояло в том, чтобы выполнить проверку типов и найти ошибки на ранней стадии, поэтому「类型」
является его основной особенностью. Конечно, это просто предупреждение о том, что ваш код может выполняться не так, как ожидалось, например, если вы не передаете параметры в соответствии с объявленным типом, ваш код все равно будет работать. Это существенно отличается от строго типизированных языков, строго типизированные языки будут напрямую приводить к сбою компиляции, т.к.TypeScript
Просто перевод.
и
JavaScript
разные,TypeScript
используемый суффикс файла.ts
имя расширения. браузер не распознан.ts
файл, поэтому вы должны поместитьTS
код вJavaScript
код. Этот процесс преобразования называется转译
,编译
и转译
Незначительные отличия:
- Компиляция — это преобразование исходного кода на другой язык.
- Транспиляция — это преобразование исходного кода в другой язык того же уровня абстракции.
мне не нравитсяTypeScript
, потому что, на мой взгляд, это приводит к нескольким проблемам:
- увеличение затрат на обучение;
- Количество кода увеличивается;
- Сложность кода увеличивается
КонечноTypeScript
Польза от этого немалая: статическая инспекция позволяет заблаговременно найти ошибки, что действительно необходимо в сегодняшней предпроектной разработке, поскольку технический уровень членов бригады разный.TypeScript
может помочь избежать многих ошибок, конечно, если выany大法
верующие, я призываю вас быть добрыми. не используйTypeScript
и использоватьTypeScript
, предпосылкой его использования должно быть то, что он может помочь вам решить конкретную проблему.
мне снова нравитсяTypeScript
, потому что он продвинутыйJavaScript
:
TypeScript
Предоставлять новейшие и развивающиесяJavaScript
характеристики, в том числе от2015
ГодECMAScript
и функции в будущих предложениях, такие как асинхронные функции иDecorators
, чтобы помочь создавать надежные компоненты.
Если честно, видел эту штуку два года назад😂, раньше всегда читал кусками, никак не могу осилить продвинутые части, в этот раз надо и нужно уладить.
~ Увы, общая тенденция, эта штука не умрет, иначе чужой код не поймет.
~ После того, как я закончил продвинутую часть, я почувствовал от всего сердцаTS
Это и продвинуто, и сложно, молча проливая слезы овощей.
текст
базовый тип
Восемь встроенных типов JS
- строка (строка)
- номер
- логический
- неопределенный
- ноль ноль)
- объект
- Большое целое число (bigInt, новое в ES6)
- символы (символ, новое в ES6)
TS
соответствующийexample
(с пробелами после двоеточия или без):
let name: string = "bob";
let age: number = 37;
let isDone: boolean = false;
let u: undefined = undefined;
let n: null = null;
let obj: object = {x: 1};
let bigLiteral: bigint = 100n;
let sym: symbol = Symbol("me");
Array
Есть два способа определить тип массива:
// 元素类型[]
let list: number[] = [1, 2, 3];
// Array<元素类型>
let list: Array<number> = [1, 2, 3];
Определите массив указанных членов объекта:
interface MyObject {
name: string;
age: number;
}
let arr: MyObject[] = [{name: "兔兔", age: 18}] // OK
Tuple
Приведенный выше метод определения типа массива может определить только массив, внутренняя часть которого принадлежит определенному типу. Массивы различных внутренних типов могут быть определены с использованием типов кортежей:
let x: [string, number];
x = ['hello', 10]; // OK
x = [10, 'hello']; // Error
Обратите внимание, что тип кортежа может представлять только массив с известным числом и типом элементов, длина которого указана, а доступ за пределами границ вызовет ошибку. Например, в массиве может быть несколько типов, число и тип неопределенны, тогда сразуany[]
.
неопределенный и нулевой
Обратите внимание, что эти два являются особенными
по умолчанию
null
иundefined
является подвидом всех типов. То есть можно поставитьnull
иundefined
Присвоить любой тип переменной.
eg:
let str: string = 'hello';
str = null; // OK
str = undefined; // OK
let a: null = undefined; // OK
let b: undefined = null; // OK
Конечно, вы также можете указать--strictNullChecks
флаг, чтобы включить строгую проверку режима. В этой ситуации,null
иundefined
Это отношение равенства с другими типами и может быть назначено толькоany
и их соответствующие типы, за одним исключениемundefined
также может быть назначеноvoid
Тип (думаю, вы объявляете возвращаемый тип для функции какvoid
, но функция явно неreturn
В случае возвращается значение по умолчаниюundefined
, что теперь является проявлением этого исключения).
void
void
Указывает, что типа нет, он равен другим типам и не может быть напрямую назначен:
let a: void;
let b: number = a; // Error
ты можешь только дать этоnull
(только в--strictNullChecks
если не указано) иundefined
. объявить одинvoid
Переменные типа не очень полезны, и мы обычно объявляем их только тогда, когда функция не возвращает значение.
Стоит отметить, что методы без возвращаемого значения получатundefined
, но нам нужно определить его какvoid
введите вместоundefined
тип. В противном случае будет сообщено об ошибке:
function fun(): undefined {
console.log("this is TypeScript");
};
fun(); // Error
любой и неизвестный
any
пропустит проверку типа для значения, любое значение может быть присвоеноany
тип, его часто называютtop type
, значит будетany
Дафа – хорошее изречение.
let notSure: any = 4;
notSure = "maybe a string instead"; // OK
notSure = false; // OK
unknown
иany
Например, все типы могут быть назначеныunknown
:
let notSure: unknown = 4;
notSure = "maybe a string instead"; // OK
notSure = false; // OK
unknown
иany
Самая большая разница:
unknown
даtop type
(любой тип егоsubtype
) , иany
обаtop type
, Сноваbottom type
(это любой видsubtype
) , что приводит кany
В основном отказ от любой проверки типов.
так какany
обаtop type
, Сноваbottom type
, так что любой тип значения может быть присвоенany
,в то же времяany
Значение типа также может быть присвоено любому типу. ноunknown
Толькоtop type
, ему можно присвоить любой тип значения, но присвоить его можно толькоunknown
иany
, потому что только два из нихtop type
.
let notSure: unknown = 4;
let uncertain: any = notSure; // OK
let notSure: any = 4;
let uncertain: unknown = notSure; // OK
let notSure: unknown = 4;
let uncertain: number = notSure; // Error
Если вы не сузите тип, вы не сможетеunknown
Введите, чтобы сделать что-нибудь:
function getDog() {
return '123'
}
const dog: unknown = {hello: getDog};
dog.hello(); // Error
Этот механизм очень превентивен и безопасен, что требует, чтобы мы сузили тип, мы можем использоватьtypeof
,类型断言
и т. д., чтобы сузить неизвестный диапазон:
function getDogName() {
let x: unknown;
return x;
};
const dogName = getDogName();
// 直接使用
const upName = dogName.toLowerCase(); // Error
// typeof
if (typeof dogName === 'string') {
const upName = dogName.toLowerCase(); // OK
}
// 类型断言
const upName = (dogName as string).toLowerCase(); // OK
never
never
Типы представляют типы значений, которые никогда не существуют.
Две ситуации, когда значение никогда не будет существовать:
-
Если функция выполняется и бросаетаномальный, то у этой функции никогда не будет возвращаемого значения (поскольку выбрасывание исключения приведет к прямому прерыванию программы, из-за чего программа будет работать меньше, чем возвращаемое значение, то есть она имеет недостижимую конечную точку, и возврата никогда не будет) ;
-
Код в функции, которая выполняет бесконечный цикл (бесконечный цикл), так что программа никогда не сможет дойти до шага, на котором функция возвращает значение, и никогда не будет возврата.
// 异常
function err(msg: string): never { // OK
throw new Error(msg);
}
// 死循环
function loopForever(): never { // OK
while (true) {};
}
never
того же типаnull
иundefined
Точно так же он также является подтипом любого типа и может быть присвоен любому типу:
let err: never;
let num: number = 4;
num = err; // OK
но типа нетnever
подтип или назначаемыйnever
типа (кромеnever
себя), даже еслиany
не может быть назначеноnever
:
let ne: never;
let nev: never;
let an: any;
ne = 123; // Error
ne = nev; // OK
ne = an; // Error
ne = (() => { throw new Error("异常"); })(); // OK
ne = (() => { while(true) {} })(); // OK
Особенности:never
После объединения с другими типами нетnever
из
// type Eg2 = string | number
type Eg2 = string | number | never
утверждение типа
Утверждения типов похожи на преобразования типов в других языках. Преобразования типов обычно происходят, когда вы болееTS
При изучении более подробной информации о значении.
Два способа достижения:
// 尖括号 语法
let someValue: any = "this is a string";
let strLength: number = (<string>someValue).length;
// as 语法
let someValue: any = "this is a string";
let strLength: number = (someValue as string).length;
Вывод типа
Если тип явно не указан, тоTypeScript
Тип выводится в соответствии с правилами вывода типов.
следующее:
let myFavoriteNumber = 'seven';
myFavoriteNumber = 7; // Error
ЗачемError
, потому что на самом деле это эквивалентно:
let myFavoriteNumber: string = 'seven';
myFavoriteNumber = 7; // Error
TypeScript
Тип выводится, когда нет явно указанного типа, что является выводом типа.
Если при его определении нет присвоения, оно будет выводиться какany
введите без проверки типа вообще:
let myFavoriteNumber;
myFavoriteNumber = 'seven';
myFavoriteNumber = 7;
тип союза
Тип объединения указывает, что значение может быть одним из нескольких типов, используя|
Отделить каждый тип.
let myFavoriteNumber: string | number;
myFavoriteNumber = 'seven'; // OK
myFavoriteNumber = 7; // OK
перекрестный тип
Кросс-тип представляет собой объединение нескольких типов в один тип. Это позволяет нам объединять существующие типы в один тип, содержащий все необходимые свойства типа, используя&
Определите тип перекрестка.
interface A {
name: string,
age: number
}
interface B {
name: string,
gender: string
}
let a: A & B = { // OK
name: "兔兔",
age: 18,
gender: "男"
};
a
обаA
типа, такжеB
тип.
Примечание: объединение нескольких типов принимается за тип пересечения, но еслиkey
одинаковые, но разные типы,key
заnever
тип.
type A = string & number // A 为 never 类型
let a: A = (() => {throw new Error()})(); // OK
интерфейс
Прежде всего, давайте разберемся с концепцией интерфейса здесь простым способом: общий интерфейс определения фона — это интерфейс, вызываемый внешним интерфейсом, определяющий некоторые параметры и т. д.,TS
Интерфейс аналогичный, а также его можно понимать как определение некоторых параметров, указание какие параметры в переменной, и какой тип параметров в переменной.При использовании должны быть параметры этих соответствующих типов.Если меньше или более параметров, и тип параметра неправильный, будет сообщено об ошибке. Проще говоря, вы можете понять, что это определение более подробного типа объекта.
Первый пример:
function printLabel(labeledObj: { label: string }) {
console.log(labeledObj.label);
}
let myObj = { size: 10, label: "Size 10 Object" };
printLabel(myObj); // OK
тебе лень писатьinterface
, можно записать так. Этот способ написания является более свободным и проверяет только наличие этих необходимых свойств.
Второй пример:
interface LabeledValue {
label: string;
}
function printLabel(labeledObj: LabeledValue) {
console.log(labeledObj.label);
}
let myObj = { size: 10, label: "Size 10 Object" };
printLabel(myObj); // OK
Этот способ написания также является свободным, как и выше, в том числе из-за присваивания.
Уведомление:Запятые и точки с запятой можно использовать в типе и интерфейсе, но не в классе. Однако все три поддерживают окончания строк без символов.
Почему присваивание усложняет проверку типов
Посмотрите внимательнее на эту фразу:
Одним из основных принципов TypeScript является проверка типа структуры значения. его иногда называютопределение типа уткиилиструктурное подтипирование.
Так называемыйопределение типа уткиэто像鸭子一样走路并且嘎嘎叫的就叫鸭子
, то есть обладающие характеристиками утки думают, что это утка, то есть путем формулирования правил, определяющих, реализует ли объект этот интерфейс (разумеется, вTS
Так не сказано).
В приведенном выше коде запись объекта в параметр эквивалентна прямой передачеlabeledObj
Назначение, этот объект имеет строгое определение типа, поэтому он не может иметь больше или меньше параметров. И когда вы используете объект снаружи с другой переменнойmyObj
перенимать,myObj
Не подвергается дополнительной проверке свойств, но выводится из типа какlet myObj: { size: number; label: string } = { size: 10, label: "Size 10 Object" };
, затем поместите этоmyObj
переназначить наlabeledObj
, в настоящее время, в соответствии с совместимостью типа, два типа объектов относятся копределение типа утки, потому что у обоих естьlabel
свойства, поэтому они считаются одинаковыми, поэтому этот метод можно использовать для обхода избыточной проверки типов.
interface LabeledValue {
label: string;
}
function printLabel(labeledObj: LabeledValue) {
console.log(labeledObj.label);
}
printLabel({ size: 10, label: "Size 10 Object" }); // Error
необязательный атрибут
interface Props {
name: string;
age: number;
money?: number;
}
Необязательный атрибут — добавить имя необязательного атрибута после определения.?
символ, подтверждающий, что это свойство является необязательным.
свойство только для чтения
interface Point {
readonly x: number;
readonly y: number;
}
let p: Point = { x: 10, y: 20 };
p.x = 5; // Error
использовать перед именем свойстваreadonly
ключевое слово, чтобы указать свойство только для чтения, свойство объекта может изменить свое значение только тогда, когда объект только что создан, иconst
похоже, ноconst
Можно запретить изменение только базового типа.Для ссылочных типов можно запретить изменение только ссылочного адреса.Можно изменить внутренние свойства.Чтобы предотвратить изменение внутренних свойств ссылочного типа, следует использовать .readonly
.
ReadonlyArray
Для массивовTS
иReadonlyArray<T>
тип, который удаляет все изменяемые методы массива, поэтому можно гарантировать, что массив не может быть изменен после его создания:
let a: number[] = [1, 2, 3, 4];
let ro: ReadonlyArray<number> = a;
ro[0] = 12; // Error
ro.push(5); // Error
ro.length = 100; // Error
a = ro; // Error
В последней строке видно, что даже если весьReadonlyArray
Присвоение нормальному массиву также невозможно, и в этом случае можно использовать утверждение типа:
a = ro as number[];
Уведомление:readonly
Тип массива только для чтения, объявленный с помощьюReadonlyArray
Объявленный тип массива только для чтения, оба эквивалентны.
доказывать:
let arr1: readonly number[] = [1, 2];
let arr2: ReadonlyArray<number> = [1, 2, 3];
arr1[0] = 0; // Error
arr2[0] = 0; // Error
arr1.push(3); //Errpr
arr2.push(4); //Error
arr1 = arr2; // OK
Способы обойти дополнительные проверки свойств
-
Тип совместимый
Пример в начале был объяснен очень подробно, и используется операция присваивания, поэтому я не буду повторяться.
-
утверждение типа
Тип утверждения равнозначен тому, что вы говорите программе, что точно знаете, что делаете, тогда программа не будет дополнительно изучать естественные свойства.
interface Props { name: string; age: number; money?: number; } let p: Props = { name: "兔神", age: 25, money: -100000, girl: false } as Props; // OK
-
подпись индекса
interface Props { name: string; age: number; money?: number; [key: string]: any; } let p: Props = { name: "兔神", age: 25, money: -100000, girl: false }; // OK
произвольные свойства
TypeScript
Поддерживаются два типа подписи индекса: строки и числа.
После определения произвольного свойства тип как детерминированных, так и необязательных свойств должен быть подмножеством его типа, поскольку детерминированные и необязательные свойства также считаются типом произвольного свойства:
interface Person {
name: boolean; // Error
age?: number; // Error
sex: string; // OK
girl: undefined; // OK
[propName: string]: string;
}
Оба типа индексов могут использоваться одновременно, но возвращаемое значение числового индекса должно быть подтипом типа возвращаемого значения строкового индекса. Это связано с тем, что при использованииnumber
При индексации,JavaScript
преобразует его вstring
Затем перейдите к объекту index.
class Animal {
name: string;
}
class Dog extends Animal {
breed: string;
}
interface NotOkay {
[x: number]: Animal; // Error
[x: string]: Dog;
}
interface Okay {
[x: number]: Dog; // OK
[x: string]: Animal;
}
Еще одна вещь, на которую мы должны обратить внимание, когда какой-либо атрибут использует тип объединения и в атрибуте есть необязательные атрибуты, объединение должно бытьundefined
type, иначе компиляция сообщит об ошибке по понятным причинам, т.к. необязательные атрибуты необязательны:
interface Props {
name: string;
age: number;
money?: number; // 这里真实的类型应该为:number | undefined
[key: string]: string | number | undefined;
}
let p: Props = {
name: "兔神",
age: 25,
money: -100000
}; // OK
Кроме того, вы можете сделать любое свойство доступным только для чтения, предотвращая присвоение свойств.
интерфейс наследует интерфейс
Интерфейс наследует интерфейс, используя ключевые словаextends
, Суть наследования заключается в копировании и извлечении общего кода, поэтому подинтерфейс имеет определение типа родительского интерфейса:
interface Shape {
color: string;
}
interface Square extends Shape {
sideLength: number;
}
let square: Square = { sideLength: 1 }; // Error
let square1: Square = { sideLength: 1, color: 'red' }; // OK
TS
Другой момент: интерфейсы могут наследоваться несколько раз.
interface Shape {
color: string;
}
interface PenStroke {
penWidth: number;
}
interface Square extends Shape, PenStroke {
sideLength: number;
}
let square: Square = { sideLength: 1 } // Error
let square1: Square = { sideLength: 1, color: 'red' } // Error
let square2: Square = { sideLength: 1, color: 'red', penWidth: 2 } // OK
Здесь следует отметить, что большинство языков не поддерживают множественное наследование, по понятным причинам множественное наследование может вызвать путаницу:
- Если родительский класс, унаследованный подклассом, имеет ту же переменную-член, подкласс не сможет отличить, какую переменную-член родительского класса использовать при ссылке на переменную;
- Если подкласс наследует несколько родительских классов с одним и тем же методом, и подкласс не переопределяет метод (если он переопределен, метод в подклассе будет использоваться напрямую), то при вызове этого метода невозможно будет определить какой метод родительского класса вызывать.
python
Поддержка множественного наследования, так называемого множественного наследования, сутьmixin
,JS
также доступныmixin
Реализовать множественное наследование.
существуетTS
, если два или более родительских интерфейса множественного наследования имеют одинаковые свойства, но определенные типы различаются,TS
Он сообщит об ошибке напрямую, и она не будет принятаmixin
Стратегия. В связи с этим хотелось бы задать вопрос: если вы извлекаете общий код, то почему вы везде ставите общий код?TS
Это то, что я думал, это должно прояснить вашу путаницу. Следовательно, при использовании нескольких наследований сначала убедитесь, что родительский интерфейс не имеет общих свойств или что типы, определенные общими свойствами, одинаковы.
interface Shape {
name: string;
color: string;
}
interface PenStroke {
name: number;
penWidth: number;
}
interface Square extends Shape, PenStroke { // Error
sideLength: number;
}
interface Shape {
name: string;
color: string;
}
interface PenStroke {
name: string;
penWidth: number;
}
interface Square extends Shape, PenStroke { // OK
sideLength: number;
}
let square: Square = { // OK
sideLength: 1,
color: 'red',
penWidth: 12,
name: '兔神'
}
в интерфейсеnew
существуетTS
На официальном сайте, например, видны
interface ClockConstructor {
new (hour: number, minute: number): any;
}
Этот способ написания очень запутан. Официальный сайт для использования в интерфейсеnew
Подробных инструкций тоже нет, только примеры, что отстой.
Мое понимание:new
За ним следует конструктор, который используется для создания экземпляра. И интерфейс используется для описания типа объекта, так что же это за тип объекта, который содержит конструктор? ответ类 class
.
// 例1
interface ClockConstructor {
new (hour: number, minute: number): any;
}
let C:ClockConstructor = class {} // OK
// 例2
interface CPerson {
new(name: string): Date;
}
let p: CPerson = class People extends Date {} // OK
мы не показываем заявлениеconstructor
, так что будет пустоconstructor
функция сверху. Здесь я нахожу обнаружение довольно странным:
interface ClockConstructor {
new (hour: number, minute: number): any;
}
let C:ClockConstructor = class { // OK
constructor() {}
}
let C1:ClockConstructor = class { // OK
constructor(h: number) {}
}
let C2:ClockConstructor = class { // OK
constructor(h: number, m: number) {}
}
let C3:ClockConstructor = class { // Error
constructor(h: string, m: number) {}
}
let C4:ClockConstructor = class { // Error
constructor(h: number, m: number, b: number) {}
}
Что мы видим в механизме обнаружения здесь, так это то, что параметров мало, и есть много совместимых параметров, которые не являются строгими. Я был очень озадачен.Когда я научился обратно, я узнал, что есть双向协变
Концепция чего-либо:
Ts
При сравнении параметров функции по умолчанию применяется стратегия двусторонней ковариации: только когда параметр исходной функции может быть присвоен целевой функции или наоборот, присвоение может быть выполнено успешно.
тип функции
объявление функции
function sum(x: number, y: number): number {
return x + y;
}
функциональное выражение
let mySum: (x: number, y: number) => number = function (x: number, y: number): number {
return x + y;
};
существуетTypeScript
В определении типа ,=>
Используется для представления определения функции, левая сторона — это тип ввода, который необходимо заключить в круглые скобки, а правая сторона — это тип вывода. Избегайте всеми средствамиES6
Функции стрелок перепутаны.
Определение типов функций с интерфейсами
interface SearchFunc{
(source: string, subString: string): boolean;
}
let mySearch: SearchFunc = function(source: string, subString: string) { // OK
let result = source.search(subString);
return result >-1;
};
При использовании интерфейса функционального выражения для определения функции ограничение типа слева от знака равенства может гарантировать, что количество параметров, типов параметров и типов возвращаемых значений останутся неизменными при назначении имени функции в будущем.
в функцииthis
утверждение
TypeScript
Это будет выведено с помощью анализа потока кода.this
Что должно быть в функции, мы также можем указать явно в функцииthis
Какой тип должен быть. Пример выглядит следующим образом:
interface Obj {
fn: (this: Obj, name: string) => void;
}
let obj: Obj = {
fn(name: string) {}
}
obj.fn("兔兔"); // OK
так какJavaScript
В спецификации указано, что у вас не может быть файла с именемthis
параметры, поэтомуTypeScript
Используйте это синтаксическое пространство, чтобы вы могли объявить в теле функцииthis
тип.
Примечание: этоthis
Декларация типа должна идти первой в параметре:
interface Obj {
// Error:A 'this' parameter must be the first parameter
fn: (name: string, this: Obj) => void;
}
Вот лучший пример, чтобы почувствовать:
interface Obj {
fn: (this: Obj, name: string) => void;
}
let obj: Obj = {
fn(name: string) {}
}
let rab: Obj ={
fn(name: string) {}
}
obj.fn("兔兔"); // OK
obj.fn.call(rab, "兔兔"); // OK
obj.fn.call(window, "兔兔"); // Error: this 应该为 Obj 类型
необязательный параметр
function buildName(firstName: string, lastName?: string) {
if (lastName) {
return firstName + ' ' + lastName;
} else {
return firstName;
}
}
let tomcat = buildName('Tom', 'Cat');
let tom = buildName('Tom');
Примечание. Обязательные параметры не допускаются после необязательных параметров.
значение параметра по умолчанию
function buildName(firstName: string, lastName: string = 'Cat') {
return firstName + ' ' + lastName;
}
let tomcat = buildName('Tom', 'Cat');
let tom = buildName('Tom');
остальные параметры
function push(array: any[], ...items: any[]) {
items.forEach(function(item) {
array.push(item);
});
}
let a = [];
push(a, 1, 2, 3);
перегрузка
Перегрузка позволяет функции вести себя по-разному, когда она принимает другое количество или тип аргументов.
重载
концепция обученияJAVA
(JAVA中的重载
) при контакте,JS
Нет такого понятия,TS
Перегрузку личного чувства следует назвать более函数签名重载
. Поскольку окончательная реализация функции по-прежнему зависит от типа решения для обработки, предыдущее определение функции предназначено только для точного выражения типа вывода, соответствующего типу ввода.
function reverse(x: number): number;
function reverse(x: string): string;
function reverse(x: number | string): number | string | void {
if (typeof x === 'number') {
return Number(x.toString().split('').reverse().join(''));
} else if (typeof x === 'string') {
return x.split('').reverse().join('');
}
}
встроенные объекты
ECMAScript
Встроенные объекты, предусмотренные стандартом:
String
,Number
,Boolean
,Error
,Date
,RegExp
Ждать.
let s: String = new String('兔神');
let n: Number = new Number(123);
let b: Boolean = new Boolean(1);
let e: Error = new Error('Error occurred');
let d: Date = new Date();
let r: RegExp = /[a-z]/;
DOM
иBOM
Встроенные объекты:Document
,HTMLElement
,Event
,NodeList
Ждать.
let body: HTMLElement = document.body;
let allDiv: NodeList = document.querySelectorAll('div');
document.addEventListener('click', function(e: MouseEvent) {
// Do something
});
массивоподобный объектIArguments
:
function sum() {
let args: IArguments = arguments;
}
IArguments
На самом деле это:
interface IArguments {
[index: number]: any;
length: number;
callee: Function;
}
Конечно, еще много, что можно увидеть здесьФайл определения для основной библиотеки TypeScript
введите псевдоним
Псевдоним типа — это присвоение типу определенного имени, а затем, где бы этот тип ни использовался, это имя можно использовать вместо него в качестве типа. Это просто имя, оно не создает новый тип. использоватьtype
ключевое слово для определения:
type StringType = string;
let str: StringType;
str = 'hello';
str = 123 // Error
Примечание. Псевдонимы типов не могут бытьextends
иimplements
, и не может появляться нигде справа от объявления.
type
реализовать наследование, можно использовать кросс-типыtype A = B & C & D
.
Тип строкового литерала
Тип строкового литерала используется, чтобы ограничить значение только одной из нескольких строк.
type Name = 'ALisa' | 'Bob' | 'Cola'
let name: Name = 'Alisa'; // Error ①
let name1: Name = 'ALisa'; // OK
let name2: Name = 'Bob'; // OK
let name3: Name = 'Cola'; // OK
let name4: Name = '兔兔'; // Error
Причина вышеуказанной ошибки ①:
По умолчанию,TS
будет DOM typings
Поскольку это глобальная среда выполнения, поэтому, когда мы объявляемname
когда, сDOM
глобальный вwindow
под объектомname
Недвижимость имеет такое же имя. Таким образом, сообщаетсяCannot redeclare block-scoped variable 'name'
Ошибка.
Тип перечисления Enum
Перечисление — это набор именованных целочисленных констант, которые часто встречаются в повседневной жизни, например день недели.
SUNDAY、MONDAY、TUESDAY、WEDNESDAY、THURSDAY、FRIDAY、SATURDAY
является перечислением.
Перечисление — это структура данных, с помощью которой мы можем определить некоторые константы с именами, четко выразить намерение или создать набор различных вариантов использования.TS
Поддерживаются числовые и строковые перечисления.
Перечисление номеров
Используйте перечисление, чтобы определить 7 дней в неделю:
enum Days {Sun, Mon, Tue, Wed, Thu, Fri, Sat};
Членам перечисления присваиваются значения из0
Начиная с увеличивающегося числа, мы можем получить доступ к членам перечисления так же, как к свойствам объекта:
console.log(Days.Sun) // 0
console.log(Days.Mon) // 1
......
console.log(Days.Sat) // 6
Мы также можем инициализировать элементы перечисления, тогда члены после инициализированного члена будут автоматически расти на его основе.1
:
enum Days {Sun = 1, Mon, Tue, Wed, Thu, Fri, Sat};
console.log(Days.Sun) // 1
console.log(Days.Mon) // 2
......
console.log(Days.Sat) // 7
Перечисление строк
Перечисления строк просты, назначьте каждому элементу строковый литерал напрямую:
enum Direction {
Up = "UP",
Down = "DOWN",
Left = "LEFT",
Right = "RIGHT",
}
Неоднородная нумерация
Гетерогенные перечисления То есть перечисления могут смешивать строковые и числовые члены:
enum Direction {
name = '兔兔',
age = 18
}
Члены, которые могут быть подсчитаны
Вычисляемые члены означают, что при инициализации члена перечисления его можно динамически вычислить с помощью выражений, функций и т. д. или ссылки на ранее определенный элемент перечисления-константы.
Момент, требующий большого внимания: предпосылка использования вычислимых членов состоит в том, что текущее перечисление должно быть числовым перечислением, то есть все члены должны бытьnumber
тип.
enum Direction {
None,
Read = 1 << 1,
Write = 1 << 2,
ReadWrite = Read | Write,
G = "123".length,
Age = 18,
Sex = getSex()
}
function getSex() {
return 12
}
Если константное выражение enum оценивается какNaN
илиInfinity
, на этапе компиляции будет сообщено об ошибке.
Оригинальные слова официального сайта:
It is a compile time error for constant enum expressions to be evaluated to
NaN
orInfinity
.
Но про тест не сообщит об ошибке.
перечисление времени выполнения
Перечисления — это объекты, которые реально существуют во время выполнения.Посмотрите, как перечисления выглядят при компиляции:
Узнал снова здесь, здесьLIFE
Написание в функции очень умное,E["X"] = 0; E[0] = 'X';
прямо упрощает доE[E["X"] = 0] = "X";
. Вот почему перечисления имеют обратное сопоставление, следует отметить, что члены строкового перечисления не имеют обратного сопоставления.
Поскольку перечисление — это объект, реально существующий во время выполнения, мы можем передать его функции:
enum E {
X, Y, Z
}
function f(obj: { X: number }) {
return obj.X;
}
f(E); // OK
константное перечисление
с помощью перечисленияconst
модификатор, чтобы избежать накладных расходов на дополнительный сгенерированный код и дополнительный косвенный доступ к элементам перечисления.
Это предложение может быть непростым для понимания, просто посмотрите на картинку и скажите:
Мы видим, чтоconst
После компиляции перечисление не компилируется вLIFE
Функция напрямую удаляется, а константа прямо заполняется в том месте, где она применяется. Целью этого является сохранение производительности.
своего рода
публичный модификатор
Модификатор атрибута по умолчанию имеет значениеpublic
Публичный, то есть к свойствам и методам класса можно получить доступ извне, и вы можете решить, объявлять ли его явно в соответствии с вашими личными предпочтениями.
class Animal {
public name: string;
public constructor(theName: string) { this.name = theName; }
public move(distanceInMeters: number) {
console.log(`${this.name} moved ${distanceInMeters}m.`);
}
}
частный модификатор
private
иpublic
Напротив, к приватным модификаторам, то есть к свойствам и методам класса, нельзя получить доступ извне.
class Animal {
public static age: number = 18;
private static title: string = '兔兔';
}
Animal.age; // OK
Animal.title; // Error
защищенный модификатор
protected
модификатор сprivate
Модификаторы ведут себя аналогично, с одним отличием,protected
члены вПроизводный класспо-прежнему доступен. Обратите внимание, вотв производном классе, вместоэкземпляр, экземпляр подкласса.
пример 1:
class Animal {
private age: number = 18;
protected title: string = '兔兔';
}
class Dog extends Animal {
getAge() {
console.log(this.age) // Error
}
getTitle() {
console.log(this.title) // OK
}
}
Пример 2:
class Animal {
private static age: number = 18;
protected static title: string = '兔兔';
}
class Dog extends Animal {
getAge() {
console.log(Dog.age) // Error
}
getTitle() {
console.log(Dog.title) // OK
}
}
свойства параметра
Мы также можем использовать параметр внутреннего метода классаpublic、private、protected
Модификатор, его роль заключается в том, чтобы упростить нам определение и инициализацию члена в одном месте.
class Animal {
constructor(public name: string, private age: number, protected sex: string) {}
}
Эквивалентно:
class Animal {
public name: string;
private age: number;
protected sex: string;
constructor(name: string, age: number, sex: string) {
this.name = name;
this.age = age;
this.sex = sex;
}
}
доказывать:
class Animal {
constructor(public name: string) {}
getName() {
console.log(this.name)
}
}
let animal = new Animal('兔兔');
animal.getName(); // "兔兔"
абстрактный класс
Абстрактные классы используются в качестве базовых классов для других производных классов и не могут быть созданы. В отличие от интерфейсов, абстрактные классы могут содержать детали реализации членов.
abstract
Ключевые слова используются для определения абстрактных классов и определения абстрактных методов в абстрактных классах.
abstract class Animal {
abstract makeSound(): void;
move(): void {
console.log("roaming the earth...");
}
}
let animal = new Animal(); // Error: 抽象类不允许被实例化
Абстрактные методы абстрактного класса не содержат конкретной реализации и должны быть реализованы в производном классе. Методы абстрактного синтаксиса и методы интерфейса. Оба являются определенными сигнатурами методов, но не имеют тела. Однако он должен содержать абстрактные методыabstract
ключевое слово и может содержать модификаторы доступа.
abstract class Department {
constructor(public name: string) {
}
printName(): void {
console.log('Department name: ' + this.name);
}
abstract printMeeting(): void; // 必须在派生类中实现
}
class AccountingDepartment extends Department {
constructor() {
super('Accounting and Auditing'); // 在派生类的构造函数中必须调用 super()
}
printMeeting(): void {
console.log('The Accounting Department meets each Monday at 10am.');
}
generateReports(): void {
console.log('Generating accounting reports...');
}
}
let department: Department; // OK:允许创建一个对抽象类型的引用
department = new Department(); // Error: 不能创建一个抽象类的实例
department = new AccountingDepartment(); // OK:允许对一个抽象子类进行实例化和赋值
department.printName(); // OK
department.printMeeting(); // OK
department.generateReports(); // Error: 方法在声明的抽象类中不存在
класс реализует интерфейс
и
C#
илиJava
Основная функция интерфейса такая же,TypeScript
Интерфейсы также можно использовать для явного принуждения класса к соблюдению контракта.
То есть мы также можем использовать классы для реализации интерфейсов, используя здесь ключевые словаimplements
:
interface Title{
title: string;
}
class title implements Title{
title: string = '兔兔';
age: number = 18; // 在实现接口的基础上,也可以添加其他的属性和方法
}
Класс может реализовывать несколько интерфейсов:
interface Age {
age: number;
}
interface Title{
title: string;
}
class title implements Title, Age{
title: string = '兔兔';
age: number = 18;
}
Разница между абстрактным классом и интерфейсом
Класс может наследовать абстрактный класс, а класс также может реализовывать интерфейс.В этих двух ситуациях я всегда чувствую, что абстрактный класс очень похож на интерфейс, поэтому давайте проанализируем его.
Абстрактные классы используются для захвата общих характеристик подклассов, тогда как интерфейсы представляют собой наборы абстрактных методов; абстрактные классы не могут быть созданы, они могут использоваться только как суперклассы подклассов и используются для создания подклассов в шаблоне иерархии наследования, в то время как интерфейс — это просто форма, интерфейс сам по себе ничего не может.
Во-вторых, абстрактные классы могут иметь реализации методов по умолчанию.Подклассы используют ключевое слово extends для наследования абстрактных классов.Если подклассы не являются абстрактными классами, они должны предоставлять реализации всех объявленных методов в абстрактных классах. Интерфейс полностью абстрактный, в нем нет реализации методов, подклассы используют ключевое слово Implements для реализации интерфейса, он должен обеспечить реализацию всех объявленных методов в интерфейсе.
статическая часть и часть экземпляра
Сначала посмотрите на пример: определите интерфейс с сигнатурой конструктора и попытайтесь реализовать этот интерфейс:
interface Person {
new(name: string)
}
class People implements Person {
constructor(name: string) {
// ...
}
}
// 报错:no match for the signature 'new (name: string): any'.
Это потому что:Когда класс реализует интерфейс, проверяется тип только экземплярной части.,иconstructor
существует в статическом разделе, поэтому не проверяется. (Здесь статическая часть относится к конструктору, причина в том, что статические свойства или статические методы напрямую связаны с конструктором)
Итак, сделайте следующее:
// 针对类构造函数的接口
interface CPerson {
new(name: string);
}
// 针对类的接口
interface IPerson {
name: string;
age: number;
}
function create(c: CPerson, name: string): IPerson {
return new c(name);
}
class People implements IPerson {
name: string;
age: number;
// 这里未声明 构造函数,根据 ES6 规定会有默认的顶上来
}
let p = create(People, 'funlee'); // 可以
Подход здесь состоит в том, чтобы определить новый интерфейс без подписи конструктора и позволить классу реализовать этот интерфейс.Цель состоит в том, чтобы использовать принцип совместимости типов для проверки типа конструктора, поэтому мы также можем использовать функциональные выражения для записи :
interface CPerson {
new(name: string):any;
}
interface IPerson {
name: string;
age: number;
}
let p: CPerson = class People implements IPerson {
name: string;
age: number;
constructor(name: string) {}
}
Класс наследования интерфейса
class Point {
x: number;
y: number;
constructor(x: number, y: number) {
this.x = x;
this.y = y;
}
}
interface Point3d extends Point {
z: number;
}
let point3d: Point3d = {x: 1, y: 2, z: 3};
Какие? Интерфейс еще может наследовать класс, что это за операция. До сих пор мы знали, что интерфейсы могут наследовать только интерфейсы, потому что они относятся к одной и той же категории.Для классов наследования интерфейсов официальное объяснение таково:существуетTS
При объявлении класса он также объявляет тип экземпляра класса..
Итак, мы можем объявить переменную какGreeter
тип:let greeter: Greeter = new Greeter("world");
, здесь после двоеточияGreeter
На данный момент он существует как тип экземпляра класса.new
НазадGreeter
существует как конструктор.
Делая шаг вперед, мы знаемclass
Суть в томfunction
Синтаксический сахар для:
class Greeter {
greeting: string;
constructor(message: string) {
this.greeting = message;
}
greet() {
return "Hello, " + this.greeting;
}
}
перевести наES5
:
"use strict";
var Greeter = /** @class */ (function () {
function Greeter(message) {
this.greeting = message;
}
Greeter.prototype.greet = function () {
return "Hello, " + this.greeting;
};
return Greeter;
}());
тип экземпляра этого классаGreeter
соответствующий переводуES5
в конструктореGreeter
Тип экземпляра , поскольку он относится к типу экземпляра, поэтому, когда интерфейс наследует класс, конструктор, статические свойства и статические методы не включаются (конечно, тип экземпляра не должен включать конструктор, статические свойства или статические методы). методы):
class Point {
/** 静态属性,坐标系原点 */
static origin = new Point(0, 0);
/** 静态方法,计算与原点距离 */
static distanceToOrigin(p: Point) {
return Math.sqrt(p.x * p.x + p.y * p.y);
}
/** 实例属性,x 轴的值 */
x: number;
/** 实例属性,y 轴的值 */
y: number;
/** 构造函数 */
constructor(x: number, y: number) {
this.x = x;
this.y = y;
}
/** 实例方法,打印此点 */
printPoint() {
console.log(this.x, this.y);
}
}
Тип, объявленный одновременно, эквивалентен:
interface PointInstanceType {
x: number;
y: number;
printPoint(): void;
}
let p1: Point;
let p2: PointInstanceType; // p1 的类型与 p2 等价
Я считаюКласс наследования интерфейса, впредь пользоваться не буду, не надо возиться.
Дженерики
Обобщения относятся к функции, при которой при определении функции, интерфейса или класса конкретный тип не указывается заранее, но тип указывается при его использовании.
общее определение
Использование дженериков<类型变量>
определение:
function identity<T>(arg: T): T {
return arg;
}
Также возможно определить сразу несколько параметров типа:
function swap<T, U>(tuple: [T, U]): [U, T] {
return [tuple[1], tuple[0]];
}
swap([7, 'seven']); // ['seven', 7]
Используйте общие переменные
function identity<T>(arg: T): T {
return arg;
}
// 使用
identity<number>(1); // OK:明确的指定`T`是`number`类型
identity(1); // OK:让编译器自己推断类型
общие ограничения
Когда универсальная переменная используется внутри функции, ее свойствами или методами нельзя манипулировать произвольно, поскольку заранее неизвестно, какого она типа.
function loggingIdentity<T>(arg: T): T {
console.log(arg.length); // Error
return arg;
}
В приведенном выше примере общийT
не обязательно содержит атрибутыlength
, поэтому при компиляции сообщается об ошибке.
Решение:
// 1
function loggingIdentity<T>(arg: T[]): T[] {
console.log(arg.length); // OK
return arg;
}
// 2
function loggingIdentity<T>(arg: Array<T>): Array<T> {
console.log(arg.length); // OK
return arg;
}
Приведенные выше два способа записи по существу указывают на то, что параметрArray
тип, поэтому вы можете использоватьlength
Атрибуты.
Утверждения типа не допускаются для универсальных типов, поскольку дженерики представляют любой или все типы, например.string
Тип не может быть утвержден какobject
тип. Итак, чтобы наложить ограничения на дженерики, нам нужно использовать интерфейс наследования для достижения:
interface Lengthwise {
length: number;
}
function loggingIdentity<T extends Lengthwise>(arg: T): T {
console.log(arg.length); // OK
return arg;
}
loggingIdentity({length: 10, value: 3}); // OK
loggingIdentity([1,2]); // OK
универсальный интерфейс
interface GenericIdentityFn {
<T>(arg: T): T;
}
function identity<T>(arg: T): T {
return arg;
}
let myIdentity: GenericIdentityFn = identity;
Мы можем увеличить общий параметр до имени интерфейса:
interface Person<T> {
name: T;
getAge(arg: T): T;
}
let myIdentity: Person<string> = {
name: "兔兔",
getAge(name) {
return name
}
};
универсальный класс
Подобно универсальным интерфейсам, дженерики также могут использоваться в определении типа класса:
class GenericNumber<T> {
zeroValue: T;
add: (x: T, y: T) => T;
}
let myGenericNumber = new GenericNumber<number>();
Следует отметить одну вещь: универсальный класс относится к типу части экземпляра, поэтому статические свойства класса не могут использовать универсальный тип.
class GenericNumber<T> {
name: T;
static zeroValue: T; // Error
add: (x: T, y: T) => T;
constructor(name: T) {
this.name = name;
}
}
Типы по умолчанию для универсальных параметров
существуетTypeScript 2.3
Позже мы можем указать типы по умолчанию для параметров типа в дженериках. Этот тип по умолчанию работает, когда дженерики используются без указания параметра типа непосредственно в коде, и его нельзя вывести из фактического параметра значения.
function createArray<T = string>(length: number, value: T): Array<T> {
let result: T[] = [];
for (let i = 0; i < length; i++) {
result[i] = value;
}
return result;
}
тип гвардии
Типы Union подходят для ситуаций, когда значения могут быть разных типов. Но что, если мы хотим точно знать, что представляет собой параметр определенного типа?
interface A {
name: string;
age: number;
}
interface B {
sex: string;
home: string;
}
function doSomething(person: A | B): void {
if(person.name) { // Error
// ...
}
}
Приведенный выше способ записи приводит к ошибке компиляции, потому что нет уверенности, что во время выполненияperson
ТипA
все ещеB
, вы можете подумать, что даже если типB
час,name
свойство не существует, также возвращаетundefined
чтобы не вводить нынешнее суждение, которое верно, но вотTS
, для атрибутов, не определенных в типе, доступ сообщит об ошибке, которую можно назватьпроверка типов. КонечноA
иB
Доступ к общим свойствам можно получить обычным образом, потому что они есть у всех.
Чтобы сообщить программе, что мы знаем, что они делают, подтвердите, что мы используем, чтобы заставить ее работать:
function doSomething(person: A | B): void {
if((person as A).name) { // OK
// ...
}
}
Недостатки этого способа очевидны, то есть нам приходится многократно использовать утверждения типа при необходимости доступа к свойствам типа.Тип защитный механизмЧтобы помочь нам с такими проблемами, охранники типов — это выражения, которые проверяются во время выполнения, чтобы гарантировать, что тип находится в определенной области. Эта концепция звучит похожеJS
серединаwith
синтаксис, но здесь он ограничен типом в области видимости.
Охранники пользовательского типа
Использование определения типа
Чтобы определить защиту типа, мы просто определяем функцию, возвращаемое значение которой представляет собойпредикат типа:
function isA(person: A | B): person is A{
return(person as A).name !== undefined;
}
// 使用
function doSomething(person: A | B): void {
if(isA(person)) { // OK
// ...
}
}
Так называемыйпредикат типа, что значитparameterName is Type
,иparameterName
Должно быть имя параметра из текущей сигнатуры функции.
Приведенный выше код понимается так: возвращаемое значение равноtrue
час,person is A
учредил.
Этот способ написания слишком болезненный, потому что необходимо определить дополнительную функцию, чтобы судить, является ли это указанным типом.Если есть несколько типов, которые нужно оценить, необходимо дополнительно определить соответствующее количество функций.Каковы преимущества этого способа письма? Преимущество, упомянутое выше, заключается в том, что типы в области могут быть ограничены.
Простой пример для иллюстрации:
function isString1(test: any): test is string{
return typeof test === "string";
}
function isString2(test: any): boolean{
return typeof test === "string";
}
Вот два примера, один из которых возвращает предикат типа, а другой возвращает логическое значение, используя:
function doSomething(param: any): void {
if(isString1(param)) {
console.log(param.toFixed(2)); // 编译时 Error
}
if(isString2(param)) {
console.log(param.toFixed(2)); // 编译时 OK,但运行时 Error
}
}
doSomething("兔兔");
Первыйif
Условные суждения используют предикаты типа, поэтому в этомif
в областиparam
тип ограниченstring
,string
тип нетtoFixed()
метод, поэтому ошибка компиляции;
секундаif
Условное суждение использует возвращаемое значение логического типа,param
Тип не ограничиваетсяstring
,В настоящее времяparam
имеет типany
,иany
Проверка типов пропускается, поэтому компиляция проходит.
использоватьin
оператор
in
Оператор используется для поиска свойств объекта, обратите внимание, что он найдет свойства в цепочке прототипов. мы обычно используемin
Больше всего места должно быть при обходе объектаfor...in...
цикл.
Здесь мы используемin
Также хорошей стратегией является определение того, существует атрибут или нет:
function doSomething(person: A | B): void {
if("name" in person) { // OK
// ...
}
}
typeof
тип гвардии
in
хорошо, этоtypeof
Естественно тоже. Фактически используется предыдущая перегрузка функцийtypeof
Тип охранник.
function padLeft(value: string, padding: string | number) {
if (typeof padding === "number") { // OK
return Array(padding + 1).join(" ") + value;
}
if (typeof padding === "string") { // OK
return padding + value;
}
throw new Error(`Expected string or number, got '${padding}'.`);
}
будь осторожен:typeof
Распознаются только две формы типгардов:typeof v === "typename"
иtypeof v !== "typename"
,"typename"
должно быть"number"
,"string"
,"boolean"
или"symbol"
. ноTypeScript
Не мешает вам сравнивать с другими строками, язык не распознает эти выражения как охранники типа.
instanceof
тип гвардии
typeof
вышли, чтоinstanceof
Не удивляйтесь, мы также можем использоватьinstanceof
уточнить тип. иtypeof
Точно так же здесь не приведены примеры.
keyof
Операторы запроса типа индекса
для любого типаT
,keyof T
Результатом является объединение всех имен общедоступных свойств типа:
interface Eg1 {
name: string,
readonly age: number,
}
// T1的类型实则是 "name" | "age"
type T1 = keyof Eg1
class Eg2 {
private name: string;
public readonly age: number;
protected home: string;
}
// T2实则被约束为 "age"
// 因为name和home不是公有属性,所以不能被keyof获取到
type T2 = keyof Eg2
Другой пример:
interface Eg1 {
name: string,
readonly age: number,
}
interface Eg2 {
sex: string
}
// T1的类型实则是 "name" | "age" | { sex: string }
type T1 = keyof Eg1 | Eg2
let a: T1 = "name"; // OK
let b: T1 = "age"; // OK
let c: T1 = { // OK
sex: "男"
}
Уведомление:keyof any
Результатstring | number | symbol
, Причина также очень проста для размышления. Разве это не три общих типа ключ-значение, которые у нас есть?
TypeScript 2.8действующий по поперечному типуkeyof
преобразуется для воздействия на поперечиныkeyof
Союз. другими словами,keyof (A & B)
будет преобразован вkeyof A | keyof B
. Это изменение должно исправить этоkeyof
Проблема несогласованности вывода выражений.
type A = { a: string };
type B = { b: string };
type T1 = keyof (A & B); // "a" | "b"
type T2<T> = keyof (T & B); // keyof T | "b"
type T3<U> = keyof (A & U); // "a" | keyof U
type T4<T, U> = keyof (T & U); // keyof T | keyof U
type T5 = T2<A>; // "a" | "b"
type T6 = T3<B>; // "a" | "b"
type T7 = T4<A, B>; // "a" | "b"
typeof
оператор
существуетTS
середина,typeof
Операторы могут использоваться для получения типа переменной или объекта. то естьTS
правильноtypeof
Оператор расширяется:
type Person = {
name: string;
age: number;
}
let man: Person = {
name: "兔兔",
age: 18
}
type Human = typeof man;
// type Human = {
// name: string;
// age: number;
// }
Это часто бывает полезно. Конкретный случай: я используюimport * as options from '...'
Импортировать все модули, на данный моментoptions
По умолчаниюany
тип, когда я обращаюсь к свойствам объекта с помощью индексации строк (т.е.options[key]
), это невозможно, поэтому вам нужно определить свойство, чтобы судитьkey
Существует ли защита типа в указанном объекте:
export function isValidKey(
key: string | number | symbol,
object: object // 不要忘了, any可以赋给object
): key is keyof typeof object {
return key in object
}
T[K]
оператор доступа к индексу
interface Eg1 {
name: string,
readonly age: number,
}
// string
type V1 = Eg1['name']
// string | number
type V2 = Eg1['name' | 'age']
// any
type V3 = Eg1['name' | 'age2222'] // Error
// string | number
type V4 = Eg1[keyof Eg1]
T[keyof T]
способ получитьT
всеkey
Тип объединения, состоящий из типов;T[keyof K]
образом, полученноеT
серединаkey
и существовать одновременноK
Тип объединения, состоящий из типов времени.
Примечание: если[]
серединаkey
Существует лиT
в , естьany
;так какTS
Я не знаюkey
какой тип в конечном итоге, такany
; и также сообщит об ошибке.
тип карты
TypeScript
Предоставляет способ создания новых типов из старых типов —тип карты. В сопоставленном типе новый тип таким же образом преобразует каждое свойство старого типа.
ключевое слово требуетсяin
:
interface Rabbit {
name: string;
age: number;
}
type ReadonlyRabbit<T> = { // 映射类型
readonly [K in keyof T]: T[K];
}
// 使用
let rabbit: ReadonlyRabbit<Rabbit> = {
name: "兔兔",
age: 18
}
rabbit.name = "蛋黄" // Error:readonly
Вопрос: почемуtype
может использоваться для сопоставления типов, аinterface
нет?
Ответ: Потому что это называетсяtypescript
не называетсяinterfacescript
, Хахаха, шучу. Причина в том, чтоtype
поддерживает вычисляемые свойства,interface
не поддерживается,typescript
настройки, так что перестаньте спрашивать, почему.
Давайте рассмотрим простейший тип отображения и его компоненты:
type Keys = 'option1' | 'option2';
type Flags = { [K in Keys]: boolean };
Его синтаксис такой же, как тип синтаксиса сигнатуры индекса, который используется внутренне.for .. in
. Имеет три части:
- переменная типа
K
, которое по очереди связано с каждым свойством. - строковое литеральное объединение
Keys
, который содержит коллекцию имен свойств для повторения. - Тип результата свойства.
Условный тип
Условные типы будут выполнять определение отношений типов с условным выражением, чтобы выбрать один из двух типов, используя ключевое словоextends
С тернарным оператором:
T extends U ? X : Y
Вышеупомянутый тип означает, что еслиT
может быть назначен наU
, то типX
, в противном случаеY
.
type TypeName<T> =
T extends string ? "string" :
T extends number ? "number" :
T extends boolean ? "boolean" :
T extends undefined ? "undefined" :
T extends Function ? "function" :
"object";
type T0 = TypeName<string>; // "string"
type T1 = TypeName<"a">; // "string"
type T2 = TypeName<true>; // "boolean"
type T3 = TypeName<() => void>; // "function"
type T4 = TypeName<string[]>; // "object"
Распределенные условные типы
Распределенные условные типыэтоextends
Когда предыдущий параметр является типом объединения, он будет декомпозирован (обход всех подтипов по очереди для условной оценки) для оценки типа объединения. Конечный результат затем объединяется в новый тип объединения.
Значит этоT extends U ? X : Y
,какT
имеет типA | B | C
, он будет проанализирован как(A extends U ? X : Y) | (B extends U ? X : Y) | (C extends U ? X : Y)
.
// type A1 = 1
type A1 = 'x' extends 'x' ? 1 : 2;
// type A2 = 2
type A2 = 'x' | 'y' extends 'x' ? 1 : 2;
// type A3 = 1 | 2
type P<T> = T extends 'x' ? 1 : 2;
type A3 = P<'x' | 'y'>
Посмотрите на пример, чтобы говорить, так что выше сказаноКогда параметр перед extends является типом объединения, он будет разложендолжно быть точнее, какКогда тип перед extends является универсальным типом, а универсальный тип передается как тип объединенияразложится.
- Предотвратить ключевое слово extends для функции распределения типов объединения
Если вы не хотите, чтобы вас разлагали (распределяли), подход также очень прост, вы можете обернуть следующее в простой тип кортежа:
type P<T> = [T] extends ['x'] ? 1 : 2;
// type A4 = 2
type A4 = P<'x' | 'y'>
infer
оператор
в условном типе
extends
В подоператорах допускается появлениеinfer
объявление, которое вводит переменную типа для вывода.
Информация, которую мы получили:
-
infer
оператор разрешен только вextends
в подведомственности; - Он используется для вывода переменных типа.
один пример:
// ReturnType 为内置工具类型,作用:由函数类型 T 的返回值类型构造一个类型。
type ReturnType<T> = T extends (...args: any[]) => infer R ? R : any;
type func = () => number;
type variable = () => string;
type funcReturnType = ReturnType<func>; // funcReturnType 类型为 number
type varReturnType = ReturnType<variable>; // varReturnType 类型为 string
infer
Его также можно использовать для распаковки, вот пример распаковки типов элементов в массиве:
type Ids = number[];
type Names = string[];
type Unpacked<T> = T extends (infer R)[] ? R : T;
type idType = Unpacked<Ids>; // idType 类型为 number
type nameType = Unpacked<Names>; // nameType 类型为string
infer
Есть еще одна очень важная особенность:
-
infer
Выведенные имена одинаковы, и оба находятся вковариантный, результатом вывода будеттип союза; -
infer
Выведенные имена одинаковы, и оба находятся вИнверсия, результатом вывода будетперекрестный тип.
ковариантныйиИнверсияОбъяснение можно найти здесьосновные статьи
Ковариантный пример:
// 例1
type Foo<T> = T extends { a: infer U; b: infer U } ? U : never;
type T10 = Foo<{ a: string; b: string }>; // T10类型为 string
type T11 = Foo<{ a: string; b: number }>; // T11类型为 string | number
// 例2
type ElementOf<T> = T extends (infer R)[] ? R : never;
type Tuple = [string, number];
type Union = ElementOf<Tuple>; // Union 类型为 string | number
Пример контравариантности:
type Bar<T> = T extends {
a: (x: infer U) => void;
b: (x: infer U) => void;
} ? U : never;
// type T1 = string
type T1 = Bar<{ a: (x: string) => void; b: (x: string) => void }>;
// type T2 = never
type T2 = Bar<{ a: (x: string) => void; b: (x: number) => void }>;
Тип шаблона строки
Строки шаблона могут в определенной степени ограничивать текст.ES6
аналогично, кроме${}
Встроенный — это тип, который не поддерживает вычисления:
type HTTP = `http://${string}`
type HTTPS = `https://${string}`
let a: HTTP = 'http://small-rabbit.top'; // OK
let b: HTTPS = 'https://small-rabbit.top'; // OK
В сочетании с использованием дженериков:
type EventName<T extends string> = `${T}Changed`;
type T0 = EventName<'foo'>; // 'fooChanged'
type T1 = EventName<'foo' | 'bar' | 'baz'>; // 'fooChanged' | 'barChanged' | 'bazChanged'
let t: T0 = 'fooChanged'; // OK
let a: T1 = 'fooChanged'; // OK
let b: T1 = 'barChanged'; // OK
let c: T1 = 'bazChanged'; // OK
type Concat<S1 extends string, S2 extends string> = `${S1}${S2}`;
type T = Concat<'Hello', 'World'>; // 'HelloWorld'
let t: T = 'HelloWorld'; // OK
Типы союзов в строковых шаблонах расширяются и комбинируются:
type T1 = "top" | "bottom";
type T2 = "left" | "right";
type T3 = `${T1}-${T2}`; // 'top-left' | 'top-right' | 'bottom-left' | 'bottom-right'
let t1: T3 = 'top-left'; // OK
let t2: T3 = 'top-right'; // OK
let t3: T3 = 'bottom-left'; // OK
let t4: T3 = 'bottom-right'; // OK
Чтобы упростить изменение типов строковых литералов, были также добавлены некоторые новые псевдонимы служебных типов для изменения регистра букв. Псевдоним нового типаUppercase
,Lowercase
,Capitalize
иUncapitalize
. Первые два преобразуют каждый символ в строке, последние два преобразуют только первый символ в строке.
type Cases<T extends string> = `${Uppercase<T>} ${Lowercase<T>} ${Capitalize<T>} ${Uncapitalize<T>}`;
type T = Cases<'bar'>; // 'BAR bar Bar bar'
let t: T = 'BAR bar Bar bar'; // OK
Ссылаться на
Руководство пользователя TypeScript 4.0
Ts Masters: 22 примера, подробно объясняющих самые малоизвестные продвинутые инструменты Ts