В 2020 году брат По написал«Переизучение ТС 1.0»Цикл статей, написанных всего50Несколько статей. TS стремительно развивается, и все больше и больше проектов начинают его использоватьTSДля разработки текущая версия TS была обновлена до4.4Версия.
Недавно брат Абао обучал группу темам ТС и обнаружил, что некоторые мелкие партнеры не очень хорошо владеют ТС и не имеют четкого понимания соответствующих концепций типового программирования ТС (гимнастики типов). Итак, Brother A Bao решил начать обновление«Переизучение ТС 2.0», эта тема дополнит и обновит предыдущие статьи, а также представит новые возможности TS 4.0 и выше. В то же время он также сосредоточится на некоторых основных концепциях TS, таких как безопасность типов, система структурных типов, отображаемые типы, дженерики, контравариантность/ковариантность и т. д.
существует«Переизучение ТС 2.0»Прежде чем сериал начнется, давайте ответим на несколько вопросов, чтобы разогреться, и заинтересованные друзья смогут его попробовать. Некоторые справочные ответы на эти 30 вопросов были отправлены в этотсклад👇.
Отвечая на вопросы, рассмотрите возможность использования онлайнTS Playground, выберите последнюю версию компилятора:v4.4.2.
Первый вопрос
type User = {
id: number;
kind: string;
};
function makeCustomer<T extends User>(u: T): T {
// Error(TS 编译器版本:v4.4.2)
// Type '{ id: number; kind: string; }' is not assignable to type 'T'.
// '{ id: number; kind: string; }' is assignable to the constraint of type 'T',
// but 'T' could be instantiated with a different subtype of constraint 'User'.
return {
id: u.id,
kind: 'customer'
}
}
Почему приведенный выше код выдает ошибку и как решить указанную выше проблему?
вопрос 2
В этом вопросе нам нужны параметрыa
иb
относятся к одному типу, т.a
иb
в то же время дляnumber
илиstring
тип. Когда значения их типов несовместимы, средство проверки типов TS может автоматически выводить соответствующее сообщение об ошибке.
function f(a: string | number, b: string | number) {
if (typeof a === 'string') {
return a + ':' + b; // no error but b can be number!
} else {
return a + b; // error as b can be number | string
}
}
f(2, 3); // Ok
f(1, 'a'); // Error
f('a', 2); // Error
f('a', 'b') // Ok
Вопрос 3
существуетОсвоение этих типов инструментов ТС позволяет вам разрабатывать больше с меньшими усилиямиВ этой статье Brother Abao представляет типы инструментов, встроенных в TS:Partial<T>
, его роль состоит в том, чтобы сделать все свойства типа необязательными.?
.
interface Todo {
title: string;
description: string;
}
function updateTodo(todo: Todo, fieldsToUpdate: Partial<Todo>) {
return { ...todo, ...fieldsToUpdate };
}
// lib.es5.d.ts
type Partial<T> = {
[P in keyof T]?: T[P];
};
Итак, как определитьSetOptional
Тип инструмента, который поддерживает опциональные атрибуты, соответствующие заданным ключам? Соответствующий пример использования выглядит следующим образом:
type Foo = {
a: number;
b?: string;
c: boolean;
}
// 测试用例
type SomeOptional = SetOptional<Foo, 'a' | 'b'>;
// type SomeOptional = {
// a?: number; // 该属性已变成可选的
// b?: string; // 保持不变
// c: boolean;
// }
в реализацииSetOptional
После типа инструмента, если вам интересно, вы можете продолжить реализациюSetRequired
Тип инструмента, с помощью которого можно сделать атрибуты, соответствующие указанным ключам, обязательными. Соответствующий пример использования выглядит следующим образом:
type Foo = {
a?: number;
b: string;
c?: boolean;
}
// 测试用例
type SomeRequired = SetRequired<Foo, 'b' | 'c'>;
// type SomeRequired = {
// a?: number;
// b: string; // 保持不变
// c: boolean; // 该属性已变成必填
// }
Вопрос 4
Pick<T, K extends keyof T>
Функция состоит в том, чтобы выбрать подсвойства типа и превратить их в подтипы, которые содержат некоторые свойства этого типа.
interface Todo {
title: string;
description: string;
completed: boolean;
}
type TodoPreview = Pick<Todo, "title" | "completed">;
const todo: TodoPreview = {
title: "Clean room",
completed: false
};
Итак, как определитьConditionalPick
Тип инструмента, поддержка в соответствии с указаннымCondition
условие для создания нового типа, соответствующий пример использования выглядит следующим образом:
interface Example {
a: string;
b: string | number;
c: () => void;
d: {};
}
// 测试用例:
type StringKeysOnly = ConditionalPick<Example, string>;
//=> {a: string}
Вопрос 5
определить тип инструментаAppendArgument
, добавьте параметр указанного типа к существующему типу функции, имя нового параметраx
, который будет первым параметром нового типа функции. Конкретные примеры использования следующие:
type Fn = (a: number, b: string) => number
type AppendArgument<F, A> = // 你的实现代码
type FinalFn = AppendArgument<Fn, boolean>
// (x: boolean, a: number, b: string) => number
Вопрос 6
Определите тип инструмента NativeFlat, который поддерживает выравнивание (выравнивание) типов массивов. Конкретные примеры использования следующие:
type NaiveFlat<T extends any[]> = // 你的实现代码
// 测试用例:
type NaiveResult = NaiveFlat<[['a'], ['b', 'c'], ['d']]>
// NaiveResult的结果: "a" | "b" | "c" | "d"
по окончанииNaiveFlat
После типа инструмента продолжайте реализациюDeepFlat
Тип утилиты для поддержки типов многомерных массивов:
type DeepFlat<T extends any[]> = unknown // 你的实现代码
// 测试用例
type Deep = [['a'], ['b', 'c'], [['d']], [[[['e']]]]];
type DeepTestResult = DeepFlat<Deep>
// DeepTestResult: "a" | "b" | "c" | "d" | "e"
Вопрос 7
Определите его, используя псевдоним типаEmptyObject
тип, чтобы для этого типа были разрешены только нулевые назначения объектов:
type EmptyObject = {}
// 测试用例
const shouldPass: EmptyObject = {}; // 可以正常赋值
const shouldFail: EmptyObject = { // 将出现编译错误
prop: "TS"
}
проходя черезEmptyObject
После того, как тип теста определен, давайте изменим следующееtakeSomeTypeOnly
Определение типа функции, чтобы ее параметры допускали только значения строгого типа SomeType. Конкретные примеры использования следующие:
type SomeType = {
prop: string
}
// 更改以下函数的类型定义,让它的参数只允许严格SomeType类型的值
function takeSomeTypeOnly(x: SomeType) { return x }
// 测试用例:
const x = { prop: 'a' };
takeSomeTypeOnly(x) // 可以正常调用
const y = { prop: 'a', addditionalProp: 'x' };
takeSomeTypeOnly(y) // 将出现编译错误
Вопрос 8
определениеNonEmptyArray
Тип инструмента, чтобы гарантировать, что данные не являются пустым массивом.
type NonEmptyArray<T> = // 你的实现代码
const a: NonEmptyArray<string> = [] // 将出现编译错误
const b: NonEmptyArray<string> = ['Hello TS'] // 非空数据,正常使用
Совет: Есть много решений этой проблемы, и заинтересованные друзья могут попробовать сами.
Вопрос 9
определитьJoinStrArray
Тип инструмента, используемый в соответствии с указаннымSeparator
Разделитель, объединяет тип строкового массива. Конкретные примеры использования следующие:
type JoinStrArray<Arr extends string[], Separator extends string, Result extends string = ""> = // 你的实现代码
// 测试用例
type Names = ["Sem", "Lolo", "Kaquko"]
type NamesComma = JoinStrArray<Names, ","> // "Sem,Lolo,Kaquko"
type NamesSpace = JoinStrArray<Names, " "> // "Sem Lolo Kaquko"
type NamesStars = JoinStrArray<Names, "⭐️"> // "Sem⭐️Lolo⭐️Kaquko"
Вопрос 10
реализоватьTrim
Тип утилиты для удаления типов строковых литералов. Конкретные примеры использования следующие:
type Trim<V extends string> = // 你的实现代码
// 测试用例
Trim<' semlinker '>
//=> 'semlinker'
Совет. Рассмотрите возможность сначала определить типы инструментов TrimLeft и TrimRight, а затем объединить их в тип инструмента Trim.
Вопрос 11
реализоватьIsEqual
Тип инструмента для сравнения двух типов на равенство. Конкретные примеры использования следующие:
type IsEqual<A, B> = // 你的实现代码
// 测试用例
type E0 = IsEqual<1, 2>; // false
type E1 = IsEqual<{ a: 1 }, { a: 1 }> // true
type E2 = IsEqual<[1], []>; // false
Вопрос 12
реализоватьHead
Тип утилиты для получения первого типа типа массива. Конкретные примеры использования следующие:
type Head<T extends Array<any>> = // 你的实现代码
// 测试用例
type H0 = Head<[]> // never
type H1 = Head<[1]> // 1
type H2 = Head<[3, 2]> // 3
Совет: Есть много решений этой проблемы, и заинтересованные друзья могут попробовать сами.
Вопрос 13
реализоватьTail
Тип инструмента, используемый для получения остальных типов типов массивов, кроме первого типа. Конкретные примеры использования следующие:
type Tail<T extends Array<any>> = // 你的实现代码
// 测试用例
type T0 = Tail<[]> // []
type T1 = Tail<[1, 2]> // [2]
type T2 = Tail<[1, 2, 3, 4, 5]> // [2, 3, 4, 5]
Совет: Есть много решений этой проблемы, и заинтересованные друзья могут попробовать сами.
Вопрос 14
реализоватьUnshift
Тип инструмента, используемый для преобразования указанного типаE
добавлен в качестве первого элемента вT
в типе массива. Конкретные примеры использования следующие:
type Unshift<T extends any[], E> = // 你的实现代码
// 测试用例
type Arr0 = Unshift<[], 1>; // [1]
type Arr1 = Unshift<[1, 2, 3], 0>; // [0, 1, 2, 3]
Совет: Есть много решений этой проблемы, и заинтересованные друзья могут попробовать сами.
Вопрос 15
реализоватьShift
Тип инструмента для удаленияT
Первый тип в типе массива. Конкретные примеры использования следующие:
type Shift<T extends any[]> = // 你的实现代码
// 测试用例
type S0 = Shift<[1, 2, 3]> // [2, 3]
type S1 = Shift<[string,number,boolean]> // [number,boolean]
Вопрос 16
реализоватьPush
Тип инструмента, используемый для преобразования указанного типаE
добавлен в качестве последнего элемента вT
в типе массива. Конкретные примеры использования следующие:
type Push<T extends any[], V> = // 你的实现代码
// 测试用例
type Arr0 = Push<[], 1> // [1]
type Arr1 = Push<[1, 2, 3], 4> // [1, 2, 3, 4]
Вопрос 17
реализоватьIncludes
Тип инструмента, используемый для определения указанного типаE
входит вT
в типе массива. Конкретные примеры использования следующие:
type Includes<T extends Array<any>, E> = // 你的实现代码
type I0 = Includes<[], 1> // false
type I1 = Includes<[2, 2, 3, 1], 2> // true
type I2 = Includes<[2, 3, 3, 1], 1> // true
Совет: Есть много решений этой проблемы, и заинтересованные друзья могут попробовать сами.
Вопрос 18
реализоватьUnionToIntersection
Тип утилиты для преобразования типов объединения в типы пересечения. Конкретные примеры использования следующие:
type UnionToIntersection<U> = // 你的实现代码
// 测试用例
type U0 = UnionToIntersection<string | number> // never
type U1 = UnionToIntersection<{ name: string } | { age: number }> // { name: string; } & { age: number; }
Вопрос 19
реализоватьOptionalKeys
Тип утилиты для получения необязательных свойств, объявленных в типе объекта. Конкретные примеры использования следующие:
type Person = {
id: string;
name: string;
age: number;
from?: string;
speak?: string;
};
type OptionalKeys<T> = // 你的实现代码
type PersonOptionalKeys = OptionalKeys<Person> // "from" | "speak"
Совет: Есть много решений этой проблемы, и заинтересованные друзья могут попробовать сами.
Вопрос 20
реализоватьCurry
Тип инструмента для каррирования типов функций. Конкретные примеры использования следующие:
type Curry<
F extends (...args: any[]) => any,
P extends any[] = Parameters<F>,
R = ReturnType<F>
> = // 你的实现代码
type F0 = Curry<() => Date>; // () => Date
type F1 = Curry<(a: number) => Date>; // (arg: number) => Date
type F2 = Curry<(a: number, b: string) => Date>; // (arg_0: number) => (b: string) => Date
Вопрос 21
реализоватьMerge
Тип инструмента, используемый для объединения двух типов в новый тип. Второй тип (SecondType)Keys
переопределит первый тип (FirstType)Keys
. Конкретные примеры использования следующие:
type Foo = {
a: number;
b: string;
};
type Bar = {
b: number;
};
type Merge<FirstType, SecondType> = // 你的实现代码
const ab: Merge<Foo, Bar> = { a: 1, b: 2 };
Вопрос 22
реализоватьRequireAtLeastOne
тип инструмента, который создаст хотя бы один из заданныхKeys
тип, остальноеKeys
Оставьте как есть. Конкретные примеры использования следующие:
type Responder = {
text?: () => string;
json?: () => string;
secure?: boolean;
};
type RequireAtLeastOne<
ObjectType,
KeysType extends keyof ObjectType = keyof ObjectType,
> = // 你的实现代码
// 表示当前类型至少包含 'text' 或 'json' 键
const responder: RequireAtLeastOne<Responder, 'text' | 'json'> = {
json: () => '{"message": "ok"}',
secure: true
};
Вопрос 23
реализоватьRemoveIndexSignature
Тип инструмента для удаления подписей индекса из существующих типов. Конкретные примеры использования следующие:
interface Foo {
[key: string]: any;
[key: number]: any;
bar(): void;
}
type RemoveIndexSignature<T> = // 你的实现代码
type FooWithOnlyBar = RemoveIndexSignature<Foo>; //{ bar: () => void; }
Вопрос 24
реализоватьMutable
Тип инструмента для удаления всех или некоторых атрибутов типа объектаreadonly
модификатор. Конкретные примеры использования следующие:
type Foo = {
readonly a: number;
readonly b: string;
readonly c: boolean;
};
type Mutable<T, Keys extends keyof T = keyof T> = // 你的实现代码
const mutableFoo: Mutable<Foo, 'a'> = { a: 1, b: '2', c: true };
mutableFoo.a = 3; // OK
mutableFoo.b = '6'; // Cannot assign to 'b' because it is a read-only property.
Вопрос 25
реализоватьIsUnion
Тип инструмента, чтобы определить, является ли указанный тип типом объединения. Конкретные примеры использования следующие:
type IsUnion<T, U = T> = // 你的实现代码
type I0 = IsUnion<string|number> // true
type I1 = IsUnion<string|never> // false
type I2 =IsUnion<string|unknown> // false
Вопрос 26
реализоватьIsNever
Тип инструмента, чтобы определить, является ли указанный типnever
тип. Конкретные примеры использования следующие:
type I0 = IsNever<never> // true
type I1 = IsNever<never | string> // false
type I2 = IsNever<null> // false
Вопрос 27
реализоватьReverse
Служебный тип, который меняет положение элементов в типе кортежа и возвращает массив. Первый элемент кортежа становится последним, а последний элемент становится первым.
type Reverse<
T extends Array<any>,
R extends Array<any> = []
> = // 你的实现代码
type R0 = Reverse<[]> // []
type R1 = Reverse<[1, 2, 3]> // [3, 2, 1]
Вопрос 28
реализоватьSplit
Тип инструмента, согласно заданному разделителю (Delimiter), чтобы обрезать строку, содержащую разделитель. можно использовать для определенияString.prototype.split
Тип возвращаемого значения метода. Конкретные примеры использования следующие:
type Item = 'semlinker,lolo,kakuqo';
type Split<
S extends string,
Delimiter extends string,
> = // 你的实现代码
type ElementType = Split<Item, ','>; // ["semlinker", "lolo", "kakuqo"]
Вопрос 29
реализоватьToPath
Тип инструмента для размещения доступа к свойству (.
или[]
) пути преобразуются в кортежи. Конкретные примеры использования следующие:
type ToPath<S extends string> = // 你的实现代码
ToPath<'foo.bar.baz'> //=> ['foo', 'bar', 'baz']
ToPath<'foo[0].bar.baz'> //=> ['foo', '0', 'bar', 'baz']
Вопрос 30
ПолныйChainable
Определения типов, которые позволяют TS успешно делать выводыresult
Тип переменной. перечислитьoption
После метода тип текущего объекта будет непрерывно расширяться, так что вызовget
способ получения правильного типа.
declare const config: Chainable
type Chainable = {
option(key: string, value: any): any
get(): any
}
const result = config
.option('age', 7)
.option('name', 'lolo')
.option('address', { value: 'XiaMen' })
.get()
type ResultType = typeof result
// 期望 ResultType 的类型是:
// {
// age: number
// name: string
// address: {
// value: string
// }
// }
Если вы можете легко ответить на приведенные выше вопросы, ваш уровень TS должен быть довольно хорошим. Конечно, если вы все еще чувствуете себя неудовлетворенным после ответа на вышеуказанные вопросы, вы можете продолжить задавать онлайн-вопросы для практики TS, 👉typescript-exercises.github.io/. Если у вас есть хорошие практические вопросы по ТС, вы можете порекомендовать их брату Абао.
Следуйте «Дороге полного стека», чтобы прочитать 4 бесплатные электронные книги (всего более 50 000 загрузок) и 11 руководств по Vue 3 для продвинутых пользователей.
«Переизучение ТС 2.0»Начиная снова, заинтересованные друзья могут добавить WeChat брата Абао:semlinker, отправьте TS, чтобы пригласить вас присоединиться к группе для совместного обучения. Если вы хотите узнать подробный ответ, вы также можете лично пообщаться с братом Абао. Вы можете представить справочные ответы на каждый вопрос.