На сколько из этих 30 практических вопросов по ТС вы сможете ответить правильно?

внешний интерфейс JavaScript TypeScript
На сколько из этих 30 практических вопросов по ТС вы сможете ответить правильно?

В 2020 году брат По написал«Переизучение ТС 1.0»Цикл статей, написанных всего50Несколько статей. TS стремительно развивается, и все больше и больше проектов начинают его использоватьTSДля разработки текущая версия TS была обновлена ​​до4.4Версия.

Недавно брат Абао обучал группу темам ТС и обнаружил, что некоторые мелкие партнеры не очень хорошо владеют ТС и не имеют четкого понимания соответствующих концепций типового программирования ТС (гимнастики типов). Итак, Brother A Bao решил начать обновление«Переизучение ТС 2.0», эта тема дополнит и обновит предыдущие статьи, а также представит новые возможности TS 4.0 и выше. В то же время он также сосредоточится на некоторых основных концепциях TS, таких как безопасность типов, система структурных типов, отображаемые типы, дженерики, контравариантность/ковариантность и т. д.

существует«Переизучение ТС 2.0»Прежде чем сериал начнется, давайте ответим на несколько вопросов, чтобы разогреться, и заинтересованные друзья смогут его попробовать. Некоторые справочные ответы на эти 30 вопросов были отправлены в этотсклад👇.

GitHub.com/Semelinker/Ах…

Отвечая на вопросы, рассмотрите возможность использования онлайн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, чтобы пригласить вас присоединиться к группе для совместного обучения. Если вы хотите узнать подробный ответ, вы также можете лично пообщаться с братом Абао. Вы можете представить справочные ответы на каждый вопрос.