Узнайте больше об дженериках в TypeScript.
Узнайте больше об дженериках в TypeScript.
Английский | https://www.digitalocean.com/community/tutorials/how-to-use-generics-in-typescript

представлять

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

TypeScript полностью поддерживает дженерики, что обеспечивает безопасность типов для компонентов, которые принимают параметры и возвращают значения, типы которых не определены до тех пор, пока они не будут использованы позже в коде. В сегодняшней статье мы попробуем реальные примеры дженериков TypeScript и исследуем, как они используются в функциях, типах, классах и интерфейсах.

Мы также будем создавать отображаемые типы и условные типы с использованием дженериков, что поможет нам создавать компоненты TypeScript, которые можно будет гибко применять ко всем необходимым ситуациям в нашем коде.

Подготовка

представлять

TypeScript — это расширение языка JavaScript, которое использует средства проверки типов во время выполнения JavaScript и во время компиляции.

TypeScript предоставляет несколько способов представления объектов в коде, один из которых — использование интерфейсов. Интерфейсы в TypeScript имеют два сценария использования: вы можете создавать контракты, которым должны следовать классы, например члены, которые эти классы должны реализовывать, и вы можете представлять типы в своем приложении точно так же, как обычные объявления типов.

Вы можете заметить, что интерфейсы и типы имеют схожий набор функций.

На самом деле, одно почти всегда можно заменить другим.

Основное отличие состоит в том, что интерфейсы могут иметь несколько объявлений для одного и того же интерфейса, и TypeScript объединяет эти объявления, тогда как типы могут быть объявлены только один раз. Вы также можете использовать типы для создания псевдонимов для примитивных типов, таких как строки и логические значения, чего не могут сделать интерфейсы.

Интерфейсы в TypeScript — мощный способ представления структур типов. Они позволяют использовать эти структуры типобезопасным образом и одновременно документировать их, напрямую улучшая работу разработчиков.

В сегодняшней статье мы создадим интерфейсы на TypeScript, научимся их использовать и поймем разницу между обычными типами и интерфейсами.

Мы попробуем различные примеры кода, которые можно использовать в среде TypeScript или TypeScript Playground — онлайн-среде, позволяющей писать TypeScript прямо в браузере.

Подготовка

Чтобы завершить сегодняшний пример, нам нужно сделать следующее:

  • окружающая среда. мы можем выбрать программу TypeScript, чтобы последовать этому примеру. Для существования настроить на локальном компьютере,Нам, Воля, нужно подготовить следующее.
  • чтобы запустить процесс TypeScript Одновременно устанавливается среда разработки связанных пакетов. Node и НПМ (или yarn)。Учебник в этой статьесерединаиспользовать Node.js Версия для 14.3.0 и npm Версия 6.14.5 Протестировано. Хочу существовать macOS или Ubuntu 18.04 Установить на, пожалуйста, следите за тем, как существует macOS Установить на Node.js и Создать местную среду развития или Как существовать Ubuntu 18.04 Установить на Node.js изиспользовать PPA Следуйте инструкциям по установке. Если вы используетеизда относится к Linux из Windows Подсистема (WSL), это тоже работает.
  • этотснаружи,мы должнысуществоватьмашина Установить на TypeScript компилятор (ТСК). Для этого, пожалуйста, обратитесь к официальному TypeScript веб-сайт.
  • Если вы не хотите, чтобы существование создавалось на вашем локальном компьютере TypeScript среду, которую вы можете использовать официальную TypeScript Playground Давай следуй.
  • Тебе, Воля, нужно достаточно из JavaScript знания, особенно да ES6+ Грамматика, такая как деконструкция, отдых Оператор и импорт/экспорт. Если вам нужна дополнительная информация по этим темам, рекомендуется прочитать нашу статью «Как использовать». JavaScript Написание серии кода.
  • Это руководство будет относиться к поддержке TypeScript И показывать встроенные ошибки текстового редактора во всех аспектах. Это не даиспользовать TypeScript Требуется, но действительно может быть использовано больше TypeScript Функция. Чтобы воспользоваться этими преимуществами, вы можете выглядеть так: Visual Studio Code Это текстовый редактор, он полностью поддерживает из коробки. Машинопись. Вы также можете существовать TypeScript Playground середина Попробуйте эти преимущества.

Все примеры, показанные в этом руководстве, были созданы с использованием TypeScript версии 4.2.3.

Общий синтаксис

существовать Входить Дженерики Перед подачей заявки,Этот урок Воляпервыйпредставлять TypeScript синтаксис дженериков, за которым следует пример, иллюстрирующий их общее использование.

Обобщенные данные отображаются в угловых скобках. TypeScript В коде формат такой <T>,Чтосередина T Указывает на входящийизтип.<T> можно понимать как T Введите дженерики.

В этом случае Т Будет вести себя так же, как параметр в функции, как заполнитель для типа, который будет объявлен при создании экземпляра структуры. Поэтому универсальный тип, указанный в угловых скобках, также называется параметром универсального типа или просто параметром типа. В одном определении также может присутствовать несколько универсальных типов, например <T, K, A>。

Примечание. По соглашению программисты обычно называют универсальные типы одной буквой. Это не синтаксическое правило: вы можете называть дженерики, как и любой другой тип в TypeScript, но это соглашение помогает сразу донести до тех, кто читает ваш код, что универсальный тип не требует определенного типа.

Дженерикиможет появитьсясуществоватьфункция、тип、добрыйиинтерфейссередина。Этот урокнемногоназад Воляпредставлять Эти структурысерединаизкаждый,Но сейчассуществовать Воляиспользоватьодинфункциякак Пример для иллюстрации Дженерикиизбазовая грамматика。

Чтобы понять, насколько полезными могут быть дженерики, представьте, что у вас есть функция JavaScript, которая принимает два параметра: объект и массив ключей. Эта функция вернет новый объект на основе исходного объекта, но содержащий только нужные вам ключи:

Язык кода:javascript
копировать
function pickObjectKeys(obj, keys) {
  let result = {}
  for (const key of keys) {
    if (key in obj) {
      result[key] = obj[key]
    }
  }
  return result
}

В этом фрагменте кода показана функция PickObjectKeys(), которая перебирает массив ключей и создает новый объект с ключами, указанными в массиве.

Вот пример, показывающий, как использовать эту функцию:

Язык кода:javascript
копировать
const language = {
  name: "TypeScript",
  age: 8,
  extensions: ['ts', 'tsx']
}

const ageAndExtensions = pickObjectKeys(language, ['age', 'extensions'])

При этом объявляется объект, а затем используется функция PickObjectKeys() для изоляции свойств возраста и расширений. Значения ageAndExtensions следующие:

Язык кода:javascript
копировать
{
  age: 8,
  extensions: ['ts', 'tsx']
}

Если вы хотите перенести этот код на TypeScript, чтобы сделать его типобезопасным, вам необходимо использовать дженерики. Мы можем провести рефакторинг кода, добавив следующие выделенные строки:

Язык кода:javascript
копировать
function pickObjectKeys<T, K extends keyof T>(obj: T, keys: K[]) {
  let result = {} as Pick<T, K>
  for (const key of keys) {
    if (key in obj) {
      result[key] = obj[key]
    }
  }
  return result
}

const language = {
  name: "TypeScript",
  age: 8,
  extensions: ['ts', 'tsx']
}

const ageAndExtensions = pickObjectKeys(language, ['age', 'extensions'])

<T, K extends keyof T> Для функции объявлены два типа параметров, где K присваивается тип, который T Объединение ключей в .

Затем установите для параметра функции obj значение, которое представляет тип T, и установите ключ для массива, независимо от того, какой тип представляет K.

Поскольку в случае языкового объекта T возраст задается числом, а расширения — массивом строк, переменной ageAndExtensions теперь будет присвоен тип объекта со свойствами age: Number и Extensions: String[] .

Это обеспечивает тип возвращаемого значения на основе аргументов, переданных в PickObjectKeys, что дает функции гибкость в применении структуры типа до того, как она узнает конкретный тип, который ей необходимо обеспечить.

Это также повышает удобство работы разработчика при использовании функции в IDE, такой как Visual Studio Code, которая будет создавать предложения для параметра ключей на основе предоставленного вами объекта. Это показано на следующем скриншоте:

Узнайте, как TypeScript серединасоздавать Дженерикиназад,Вы сейчассуществовать Вы можете продолжить изучениесуществоватьпри определенных обстоятельствахиспользовать Дженерики。Этот урок Воляпервыйпредставлятькаксуществоватьфункциясерединаиспользовать Дженерики。

Использование дженериков с функциями

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

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

Назначьте общие параметры

Взгляните на функцию ниже, которая возвращает то, что передано в качестве первого аргумента:

Язык кода:javascript
копировать
function identity(value) {
  return value;
}

Вы можете добавить следующий код, чтобы сделать функцию типобезопасной в TypeScript:

Язык кода:javascript
копировать
function identity<T>(value: T): T{
  return value;
}

Вы делаете свою функцию универсальной функцией, которая принимает параметр универсального типа T , который является типом первого параметра, а затем устанавливает тип возвращаемого значения такой же, как : T .

Затем добавьте следующий код, чтобы опробовать эту функцию:

Язык кода:javascript
копировать
function identity<T>(value: T): T {
  return value;
}

const result = identity(123);

Результат имеет тип 123, который является точным числом, которое вы передали. Здесь TypeScript выводит общий тип из самого вызывающего кода. Таким образом, вызывающему коду не нужно передавать какие-либо параметры типа. Вы также можете явно установить для параметра универсального типа нужный тип:

Язык кода:javascript
копировать
function identity<T>(value: T): T {
  return value;
}

const result = identity<number>(123);

В этом коде результат Имеет номер типа. Используя <number> Код, переданный в типе, который вы явно разрешили TypeScript Знайте параметры универсального типа, которые вы хотите, чтобы функция идентификации имела T Тип число. Это заставит числовые типы использоваться в качестве параметров и возвращаемых значений.

Передача параметров типа напрямую

Передача параметров типа Прямосуществоватьиспользовать также полезно при настройке вашего типа. Например,Взгляните на код ниже:

Язык кода:javascript
копировать
type ProgrammingLanguage = {
  name: string;
};

function identity<T>(value: T): T {
  return value;
}

const result = identity<ProgrammingLanguage>({ name: "TypeScript" });

В этом коде результат Иметь собственный тип ProgrammingLanguage, поскольку он передается непосредственно в функцию идентификации. Если вы явно не укажете параметр типа, результат будет иметь тип { name: string } 。

Другой распространенный пример использования JavaScript — использование функций-оболочек для получения данных из API:

Язык кода:javascript
копировать
async function fetchApi(path: string) {
  const response = await fetch(`https://example.com/api${path}`)
  return response.json();
}

Эта асинхронная функция будет URL путь в качестве параметра, используйте fetch API К URL Оформить заявку и вернуть JSON значение ответа. В этом случае fetchApi Тип возвращаемого значения функции будет Promise<any>,Это правильно fetch Объект ответа вызывает json() тип возвращаемого значения.

Использование Any в качестве возвращаемого типа не очень полезно. Any означает любое значение JavaScript, используя его, вы теряете статическую проверку типов, что является одним из основных преимуществ TypeScript. Если вы знаете, что API вернет объект заданной формы, вы можете использовать дженерики, чтобы сделать эту функцию безопасной по типу:

Язык кода:javascript
копировать
async function fetchApi<ResultType>(path: string): Promise<ResultType>{
  const response = await fetch(`https://example.com/api${path}`);
  return response.json();
}

Выделенный код преобразует вашу функцию в приемную ResultType Дженерикитиппараметриз Дженерикифункция。этот Дженерикитиписпользуется дляфункцияизвозвращатьсятип:Promise<ResultType>。

Примечание. Поскольку ваша функция асинхронна, вы должны вернуть объект Promise. Тип Promise TypeScript сам по себе является универсальным типом, который принимает тип значения, к которому разрешается обещание.

Если вы внимательно посмотрите на свою функцию, вы увидите, что дженерики не используются в списке параметров или где-либо еще, где TypeScript может вывести ее значение. Это означает, что вызывающий код должен явно передавать тип этого обобщения при вызове вашей функции.

Ниже приведена возможная реализация универсальной функции fetchApi, которая извлекает пользовательские данные:

Язык кода:javascript
копировать
type User = {
  name: string;
}

async function fetchApi<ResultType>(path: string): Promise<ResultType> {
  const response = await fetch(`https://example.com/api${path}`);
  return response.json();
}

const data = await fetchApi<User[]>('/users')

export {}

В этом коде вы создаете новый тип с именем User и используете массив этого типа (User[]) в качестве типа для универсального параметра ResultType. Переменная данных теперь имеет тип User[] вместо чего-либо.

ПРИМЕЧАНИЕ. Когда вы используете await При асинхронной обработке результата функции тип возвращаемого значения будет Promise<T> середина T изтип,существовать Этот примерсерединадауниверсальный тип ResultType。

Параметры типа по умолчанию

Чтобы создать универсальную функцию fetchApi, как вы, вызывающий код всегда должен предоставлять параметры типа. Если вызывающий код не содержит универсального типа, ResultType будет привязан как неизвестный. В качестве примера возьмем следующую реализацию:

Язык кода:javascript
копировать
async function fetchApi<ResultType>(path: string): Promise<ResultType> {
  const response = await fetch(`https://example.com/api${path}`);
  return 
response.json();
}

const data = await fetchApi('/users')

console.log(data.a)

export {}

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

если ты не планируешь Воляидентификациятипдобавить в Дженерикифункцияизкаждый звоноксередина,тогда все в порядке Воляпо умолчаниютипдобавить в Дженерикитиппараметрсередина。этот Можетпройтисуществовать Дженерикитип Изназаддобавить в = DefaultType выполнить следующим образом:

Язык кода:javascript
копировать
async function fetchApi<ResultType= Record<string, any>>(path: string): Promise<ResultType> {
  const response = await fetch(`https://example.com/api${path}`);
  return response.json();
}

const data = await fetchApi('/users')

console.log(data.a)

export {}

С этим кодом вам больше не нужно звонить fetchApi функция при передаче типа в ResultType Общий параметр, поскольку он имеет тип по умолчанию. Record<string, any>。этотиметь в виду TypeScript Распознает данные как объект с ключами типа string и значениями любого типа, что позволяет получить доступ к его свойствам.

Ограничения параметров типа

В некоторых случаях параметры универсального типа должны разрешать передачу в универсальный тип только определенных фигур. Чтобы создать дополнительный уровень специализации для ваших дженериков, вы можете наложить ограничения на свои параметры.

Допустим, у вас есть ограничение на хранилище, при котором вы можете хранить только объекты, все свойства которых имеют строковые значения. Для этого можно создать функцию, которая принимает любой объект и возвращает другой объект с теми же ключами, что и исходный объект, но со всеми значениями, преобразованными в строки. Эта функция будет называться stringifyObjectKeyValues.

Эта функция будет общей функцией. Таким образом, вы можете придать полученному объекту ту же форму, что и исходный объект. Функция будет выглядеть так:

Язык кода:javascript
копировать
function stringifyObjectKeyValues<T extends Record<string, any>>(obj: T) {
  return Object.keys(obj).reduce((acc, key) =>  ({
    ...acc,
    [key]: JSON.stringify(obj[key])
  }), {} as { [K in keyof T]: string })
}

существоватьэтоткодсередина,stringifyObjectKeyValues ​​использовать метод сокращения массива, перебирающий исходный массив ключей,Значения Воля преобразуются в строки и Воля добавляется в новый массив середина.

Чтобы гарантировать, что вызывающий код всегда передает объект в вашу функцию, вы добавляете T начальствоиспользоватьтипограничение,Как подчеркнуто нижеизкодпоказано:

Язык кода:javascript
копировать
function stringifyObjectKeyValues<Textends Record<string, any>>(obj: T) {
  // ...
}

extends Record<string, any> Это ограничение, известное как ограничение универсального типа, позволяет указать, что ваш универсальный тип должен быть назначен extends Тип после ключевого слова.

существуют В этом случае,Record<string, any> Представляет объект с ключами строкового типа и значениями любого типа. Параметры типа могут расширять любые допустимые TypeScript тип.

При вызове сокращения тип возвращаемого значения функции редуктора основан на начальном значении аккумулятора. {} as { [K в ключе T]: строка } Код устанавливает тип начального значения аккумулятора как {[K в ключе T]: строка } путем приведения пустого объекта {}.

type { [K in keyof T]: string } Создайте новый тип с помощью T такой жеизключ,Но все значения установлены на тип строки,Это называется картированием,Это руководство «Волясуществовать» более подробно рассматривается в разделе середина.

Следующий код показывает реализацию функции stringifyObjectKeyValues:

Язык кода:javascript
копировать
function stringifyObjectKeyValues<T extends Record<string, any>>(obj: T) {
  return Object.keys(obj).reduce((acc, key) =>  ({
    ...acc,
    [key]: JSON.stringify(obj[key])
  }), {} as { [K in keyof T]: string })
}

const stringifiedValues = stringifyObjectKeyValues({ a: "1", b: 2, c: true, d: [1, 2, 3]})

Переменная stringifiedValues ​​будет иметь следующий тип:

Язык кода:javascript
копировать
{
  a: string;
  b: string;
  c: string;
  d: string;
}

Это гарантирует, что возвращаемое значение соответствует назначению функции.

Этот разделпредставлять Понятно Использование дженериков с Использование различных методов, включая прямое присвоение типов параметров и установку значений по умолчанию и ограничений для форм параметров.

Далее вы рассмотрите несколько примеров, чтобы увидеть, как дженерики делают интерфейсы и классы применимыми в большем количестве ситуаций.

Воля Дженерикииинтерфейс、добрыйитип Вместеиспользовать

существовать TypeScript При создании класса интерфейса может оказаться полезным задать форму результирующего объекта с помощью универсального параметра.

Например, класс может иметь разные свойства в зависимости от того, что передается конструктору. В этом разделе существует середина, вы Воля понимаете существующий класс и интерфейс середина, объявляете параметры общего типа из синтаксиса и проверяете HTTP Приложения серединаиз распространенных случаев использования.

Общие интерфейсы и классы

хотетьсоздавать Универсальныйинтерфейс,ты можешьсуществоватьинтерфейс名称Изназаддобавить втиппараметрсписок:

Язык кода:javascript
копировать
interface MyInterface<T> {
  field: T
}

Здесь объявляется интерфейс, имеющий поле свойства, тип которого определяется типом, переданным в T .

Для классов синтаксис практически такой же:

Язык кода:javascript
копировать
class MyClass<T> {
  field: T
  constructor(field: T) {
    this.field = field
  }
}

Универсальныйинтерфейс/добрыйиз Распространенный случай использованияда Когда у тебя есть поле,Чтотип Зависит от клиентакодкакиспользоватьинтерфейс/добрыйчас。

Предположим, у вас есть класс HttpApplication, который обрабатывает HTTP-запросы к API, и каждому обработчику запроса передаются определенные значения контекста. Один из способов сделать это:

Язык кода:javascript
копировать
class HttpApplication<Context> {
  context: Context
  constructor(context: Context) {
    this.context = context;
  }

  // ... implementation

  get(url: string, handler: (context: Context) => Promise<void>): this {
    // ... implementation
    return this;
  }
}

Этот класс хранит контекст, тип которого get Метод середина обрабатывает функцию на основе переданного типа параметра. существоватьиспользовать процесс середина, переданный get Обработчик параметра типа Воля правильно выводится из содержимого середина, переданного конструктору класса.

Язык кода:javascript
копировать
...
const context = { someValue: true };
const app = new HttpApplication(context);

app.get('/api', async () => {
  console.log(context.someValue)
});

существоватьэтотвыполнитьсередина,TypeScript воля context.someValue Тип определяется как Boolean.

универсальный тип

сейчассуществоватьуже Понятноразвязать Понятнодобрыйиинтерфейссередина Дженерикиизнесколько примеров,Вы сейчассуществовать Можетпродолжатьсоздавать Дженерики Настроитьтип.Воля Дженерикиприменяется ктипизграмматикадобрыйпохоже на Воля Дженерикиприменяется кинтерфейсидобрыйизграмматика。Взгляните на код ниже:

Язык кода:javascript
копировать
type MyIdentityType<T> = T

этот Дженерикитипвозвращатьсякактиппараметрпередачаизтип.Предположим, выиспользоватьнижекодвыполнить Понятноэтотдобрыйтип:

Язык кода:javascript
копировать
...
type B = MyIdentityType<number>

существуют В этом случае,тип B будет типа number。

универсальный тип часто используется для создания вспомогательного типа, особенно когда дасуществоватьиспользовать тип карты. Машинопись Предоставляется ряд готовых помощников.

Одним из таких примеров является тип Partial, который принимает тип T и возвращает другой тип той же формы, что и T, но со всеми его полями, установленными как необязательные. Реализация Partial выглядит следующим образом:

Язык кода:javascript
копировать
type Partial<T> = {
  [P in keyof T]?: T[P];
};

Вот Partial тип принимает тип, перебирая его атрибуты потом их как необязательный тип верните к новому типсередина.

ПРИМЕЧАНИЕ. Из-за Partial Уже встроен в TypeScript середина, этот код Волиэтот скомпилирован в твой компьютер TypeScript Окружающая среда середина будет пересмотрена Partial и выдает ошибку. Реализация Partial, приведенная здесь, предназначена только для иллюстрации.

хотеть Понятноразвязать Дженерикитипнасколько мощный,Допустим, у вас естьвернокак буквально,Затраты на транспортировку склада из одного магазина во все остальные магазины торговой сети вашего бизнеса. Каждый магазин Воля идентифицируется трехзначным кодом.,Как показано ниже:

Язык кода:javascript
копировать
{
  ABC: {
    ABC: null,
    DEF: 12,
    GHI: 13,
  },
  DEF: {
    ABC: 12,
    DEF: null,
    GHI: 17,
  },
  GHI: {
    ABC: 13,
    DEF: 17,
    GHI: null,
  },
}

Объект представляет местоположение магазина, объект и коллекцию. существованиекаждый магазинсередина,Есть атрибуты, указывающие стоимость доставки в другие магазины. Например,от ABC Корабль DEF Стоимость 12. Стоимость доставки из магазина самому себе равна нулю, поскольку стоимость доставки вообще отсутствует.

Чтобы гарантировать, что другие магазины имеют согласованные значения для своих местоположений и что магазины, осуществляющие доставку самим себе, всегда пусты, вы можете создать общий вспомогательный тип:

Язык кода:javascript
копировать
type IfSameKeyThanParentTOtherwiseOtherType<Keys extends string, T, OtherType> = {
  [K in Keys]: {
    [SameThanK in K]: T;
  } &
    { [OtherThanK in Exclude<Keys, K>]: OtherType };
};

IfSameKeyThanParentTOtherwiseOtherType type принимает три общих типа: первый, Keys, если вы хотите убедиться, что ваш объект имеет все ключи. В этом случае существуют все коды хранения из Union.

T да Когда поле вложенного объекта имеет тот же ключ «из», что и ключ «из» в типе родительского объекта, в этом случае оно представляет собой доставку самому себе из местоположения магазина. Наконец, Другой Тип да key Неодновременный тип, указывающий, что один магазин доставляет товары в другой магазин.

Вы можете сделать это так:

Язык кода:javascript
копировать
...
type Code = 'ABC' | 'DEF' | 'GHI'

const shippingCosts: IfSameKeyThanParentTOtherwiseOtherType<Code, null, number> = {
  ABC: {
    ABC: null,
    DEF: 12,
    GHI: 13,
  },
  DEF: {
    ABC: 12,
    DEF: null,
    GHI: 17,
  },
  GHI: {
    ABC: 13,
    DEF: 17,
    GHI: null,
  },
}

этот код сейчас существует и обеспечивает соблюдение формы типа. Если вы установили для какого-либо ключа недопустимое значение, TypeScript

Язык кода:javascript
копировать
...
const shippingCosts: IfSameKeyThanParentTOtherwiseOtherType<Code, null, number> = {
  ABC: {
    ABC: 12,
    DEF: 12,
    GHI: 13,
  },
  DEF: {
    ABC: 12,
    DEF: null,
    GHI: 17,
  },
  GHI: {
    ABC: 13,
    DEF: 17,
    GHI: null,
  },
}

Поскольку стоимость доставки между ABC и самим собой больше не равна нулю, TypeScript выдаст следующую ошибку:

Язык кода:javascript
копировать
OutputType 'number' is not assignable to type 'null'.(2322)

Вы сейчассуществовать Уже попробовалсуществоватьинтерфейс、добрыйи Настроитьвспомогательная программатипсерединаиспользовать Дженерики。Следующий,ты Воля Обсудить дальше Этот уроксерединауже多次出сейчасизтема:использовать Дженерикисоздаватькартографированиетип.

использовать Дженерикисоздаватькартографированиетип

существоватьиспользовать TypeScript Иногда вам нужно создать тип той же формы, что и другой изтип. Это означает, что он должен иметь те же свойства, но атрибуты из Тип. установлен на разные вещи. В этом случае тип карты использования может повторно использовать исходную форму типа и уменьшить дублирование кода приложения.

существовать TypeScript середина,Эта структура называется картой и опирается на дженерики. существует этот разделсередина,Вы Воля увидите, как создать тип отображения.

представлять себе,Вы хотите создать тип,задан другой тип,Тип возвращает новый тип,Все его свойства имеют логические значения. Вы можете создать этот тип с помощью следующего кода:

Язык кода:javascript
копировать
type BooleanFields<T> = {
  [K in keyof T]: boolean;
}

существоватьэтотдобрыйтипсередина,тыиспользоватьграмматика [K in keyof T] чтобы указать свойства, которые будет иметь новый тип. клавиша T оператор используется для возврата T середина Все доступные имена атрибутов из объединения. Затем используйте K in Синтаксис определяет новый атрибут typeiz, который возвращает из объединения типсередина все доступные на данный момент атрибуты. Ключ Т.

Это создаст файл с именем K изновыйтип, который привязан к текущему свойству по имени. Это можно использовать для использования грамматики T[K] Получите доступ к исходному свойству типсерединаэтот изтип.существовать, в этом случае ваше свойство Воля из Тип установлен булево значение.

этот BooleanFields типизAuse Scene да создает объект параметров. Допустим, у вас есть Модель базы данных, например пользовательская.

При получении этой модели из записи из базы данных середина вы также разрешаете передачу объекта из, который определяет, какие поля должны быть возвращены.

Объект Воля имеет те же свойства, что и модель, но Тип установлен булево значение. существует поле серединапасс true означает, что вы хотите, чтобы его вернули, тогда как false означает, что вы хотите, чтобы оно было опущено.

ты можешьсуществоватьсейчас Есть модельтипначальствоиспользовать BooleanFields Generic для возврата нового типа той же формы, что и модель, но со всеми полями, установленными на логические типы, как показано в выделенном коде ниже:

Язык кода:javascript
копировать
type BooleanFields<T> = {
  [K in keyof T]: boolean;
};

type User = {
  email: string;
  name: string;
}

type UserFetchOptions = BooleanFields<User>;

существоватьэтот Примерсередина,UserFetchOptions было бы то же самое, что создать его следующим образом:

Язык кода:javascript
копировать
type UserFetchOptions = {
  email: boolean;
  name: boolean;
}

При создании сопоставления вы также можете указать модификаторы для полей. Пример этого TypeScript середина доступна из существующего родового типа, называемого Readonly<T>。Readonly<T> типвозвращатьсяодинновыйтип,Чтосерединапередачатипиз Все свойства установлены как свойства, доступные только для чтения.。этотдобрыйтипизвыполнить Как показано ниже:

Язык кода:javascript
копировать
type Readonly<T> = {
  readonly [K in keyof T]: T[K]
}

Уведомление:потому что Readonly Уже встроен в TypeScript середина, этот код Волиэтот скомпилирован в твой компьютер TypeScript Окружающая среда середина будет пересмотрена Readonly и выдает ошибку. Реализация isReadonlyiz цитируется здесь только с целью иллюстрации isprojectiz.

Обратите внимание на модификатор только для чтения, он добавляет префикс как к этому коду серединаиз [K in keyof T] часть.

В настоящее время можно сопоставить типсерединаиспользоватьиз двух доступных модификаторов да readonly модификатор, который необходимо добавить в качестве префикса к атрибуту, и ? Модификаторы, которые можно добавлять как суффиксы к свойствам. этот ? Модификаторы отмечают поля как необязательные.

Оба модификатора могут иметь специальный префикс из, указывающий, следует ли удалять модификатор да. (-) или добавьте (+). Если указаны только модификаторы, предполагается, что это +。

Теперь вы можете использовать тип карты для создания нового типа на основе уже созданной формы изтипа.,ты можешьпродолжить обсуждение Дженерикиизокончательный вариант использования:состояниетип.

использовать Дженерикисоздаватьсостояниетип

существовать Этот разделсередина,ты Воляпытаться TypeScript середина Generics из Еще одна полезная функция: создание условий типа Прежде всего, вы Воля разбираетесь в Базовой. структура видов условий。Ранназад,Вы, Воля, исследуете расширенные варианты использования, создавая тип условия.,Тип условия исключает вложенные поля объектов, типизированных на основе точечной записи.

Базовая структура видов условий

Тип условийпо мнению некоторыхсостояниеимеют разныерезультаттипиз Дженерикитип.Например,смотри нижеиз Дженерикитип IsStringType<T>:

Язык кода:javascript
копировать
type IsStringType<T> = T extends string ? true : false;

существуетэтот код середина, вы создаете файл с именем IsStringType Новый универсальный тип, получающий один параметр типа. Т. определение вашего изтипа существует середина, синтаксис вашего использования выглядит как использовать JavaScript серединаизтернарный операторизусловное выражение: T extends string ? истинное и ложное.

это условное выражение положительное существование тип проверки T да Нет расширения строки типа. Если да, тип результата Точно соответствует типу Воля, в противном случае устанавливается значение Воля; false тип.

Уведомление:этотсостояниевыражениедасуществовать Оценивается во время компиляциииз。TypeScript Применяется только к типу, поэтому всегда указывайте идентификатор серединаиз как тип, а не значение да. существуетэтот код середина, вы используете каждое логическое значение из точного типа, true и false。

Чтобы попробовать этот условный тип, передайте какой-нибудь тип в качестве параметра типа:

Язык кода:javascript
копировать
type IsStringType<T> = T extends string ? true : false;

type A = "abc";
type B = {
  name: string;
};

type ResultA = IsStringType<A>;
type ResultB = IsStringType<B>;

существоватьэтоткодсередина,Вы создали два типа шрифта,A и Б. тип A да строковый литерал "abc" изтип, а тип B да есть имя name of type string Свойства из Объект изтип.

Затем объедините эти два типа с IsStringType Тип использования и тип результата Воля сохраняются в два новых типа. ResultA и ResultB середина。

если проверить ResultA и ResultB тип результата, вы заметите ResultA Тип установлен на точный тип правда, пока ResultB Тип установлен на ЛОЖЬ. Это правильно, потому что A расширяет тип строки, пока B Нет типа строки расширения,Поскольку для него установлена ​​строкатипизодно имя Свойства из Объект изтип.

Тип условий Полезная функция, позволяющая использовать специальные ключевые слова. infer существовать extends Предложение середина содержит информацию о типе. Затем вы можете использовать условие существования из реальной ветви серединаиспользовать этот новый тип. Эта функция из одного возможного использования позволяет получить любой тип функции. возвращаемого значения.

Чтобы проиллюстрировать это, напишите следующий тип GetReturnType:

Язык кода:javascript
копировать
type GetReturnType<T> = T extends (...args: any[]) => infer U ? U : never;

существоватьэтоткодсередина,Ты, Воля, создаешь новый из родового типа,этодаодин名为 GetReturnType изусловиетип.этот универсальный тип принимает один параметр типа T。

утверждение типа существует внутри, вы проверяете тип T даNo расширяется, чтобы соответствовать сигнатуре функции, которая принимает переменное количество аргументов (включая ноль), а затем вы получаете возврат Тип этой функции создает новый тип U,Можетсуществоватьсостояниеизвнутри реальной веткииспользовать。

U изтип Воля привязана к переданной функции из возвращаемого значения изтип. T Без функции да возвращается код Воля never тип.

используйте свой вариант следующего кода:

Язык кода:javascript
копировать
type GetReturnType<T> = T extends (...args: any[]) => infer U ? U : never;

function someFunction() {
  return true;
}

type ReturnTypeOfSomeFunction = GetReturnType<typeof someFunction>;

существуетэтот код середина, вы, Воля, создаёте файл с именем someFunction функция, которая возвращает true。Ранназадиспользовать typeof Оператор Воляэтот функция изтип передается GetReturnType Дженерики и результаты «Воля»тип магазина существуют ReturnTypeOfSomeFunction типсередина。

потому что someFunction функция переменнаяизтипда, потому что это условиетип Воля оценивает состояние реальной ветви. Это вернетсятип U Как результат.

тип U да выводится из функции, которая возвращает тип из, существующий в данном случае серединада логического типа. если проверить ReturnTypeOfSomeFunction изтипа, вы обнаружите, что для него правильно установлен логический тип.

Расширенные варианты условного использования

Тип условий TypeScript середина — одна из наиболее гибких доступных функций, позволяющая создавать некоторые расширенные типы утилит.

существует этот раздел середина, вы Воля, создав файл с названием NestedOmit<T, KeysToOmit> изусловиетип, чтобы изучить один из этих вариантов использования.

эта утилита типа Воля умеет опускать поля середина объекта, так же, как и существующая из Omit<T, KeysToOmit> Та же утилита, что и тип, но также позволяет использовать точечную нотацию для пропуска вложенных полей.

использоватьновыйиз NestedOmit<T, KeysToOmit> Дженерики,Вы можете использовать воля, как показано в следующем примере серединаизтипа:

Язык кода:javascript
копировать
type SomeType = {
  a: {
    b: string,
    c: {
      d: number;
      e: string[]
    },
    f: number
  }
  g: number | string,
  h: {
    i: string,
    j: number,
  },
  k: {
    l: number,<F3>
  }
}

type Result = NestedOmit<SomeType, "a.b" | "a.c.e" | "h.i" | "k">;

Этот код объявляет файл с именем SomeType изтип, он имеет вложенные атрибуты из многоуровневой структуры. использовать NestedOmit Generics, укажите тип, а затем перечислите атрибуты и ключи, которые следует опустить.

Пожалуйста, Уведомление о том, как существует второй параметр типа, серединаиспользовать точечную нотацию, чтобы идентифицировать ключ, который следует опустить. а потомрезультаттипхранилищесуществовать Result середина。

структураэтотсостояниетип Воляиспользовать TypeScript середина Можетиспользоватьизмножество функций,Например,Текст шаблонатип、Дженерики、Условный тип отображения.

Чтобы попробовать этот универсальный вариант, сначала создайте класс с именем NestedOmit из Дженерикитип,Он принимает два параметра:

Язык кода:javascript
копировать
type NestedOmit<T extends Record<string, any>, KeysToOmit extends string>

Первый параметр типа называется Т, это должно быть да доступно для назначения Record<string, any> типизтип.этот Волядатыхотетьотсерединаупущение Свойства из Объект изтип.

Второй параметр типа называется KeysToOmit и должен быть строковым типом. Вы хотите использовать его, чтобы указать тип. T серединаупущениеизключ。

Затем проверьте, добавив следующий выделенный код KeysToOmit даназначается {infer KeyPart1}.{infer KeyPart2} тип:

Язык кода:javascript
копировать
type NestedOmit<T extends Record<string, any>, KeysToOmit extends string>=
  KeysToOmit extends `${infer KeyPart1}.${infer KeyPart2}`

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

Выводя две части типовой строки литерала шаблона, вы разделяете строку Воля на две другие строки. Первая часть Воля отведена KeyPart1 тип, а Воля содержат все до первой точки из.

Вторая часть будет выделена KeyPart2 тип, а Воля включайте всё после первой точки из. Если ты Воля"a.b.c"как KeysToOmit перенос, затем первоначально KeyPart1 Воля установлена ​​в точности из типа строки "a", а KeyPart2 будет установлено значение «b.c».

Далее вы добавите тернарный оператор, чтобы определить первую истинную ветвь условия:

Язык кода:javascript
копировать
type NestedOmit<T extends Record<string, any>, KeysToOmit extends string> =
  KeysToOmit extends `${infer KeyPart1}.${infer KeyPart2}`
    ?
      KeyPart1 extends keyof T

Это использование KeyPart1 расширяет keyof T и проверяет KeyPart1 для данного типа T из допустимых атрибутов. Если у вас есть действительный ключ «из», добавьте следующий код, чтобы условие оценивалось как пересечение «из» между двумя типами:

Язык кода:javascript
копировать
type NestedOmit<T extends Record<string, any>, KeysToOmit extends string> =
  KeysToOmit extends `${infer KeyPart1}.${infer KeyPart2}`
    ?
      KeyPart1 extends keyof T
      ?
        Omit<T, KeyPart1>
        & {
          [NewKeys in KeyPart1]: NestedOmit<T[NewKeys], KeyPart2>
        }

Omit<T, KeyPart1> да Что-то вродеиспользовать TypeScript Поставляется по умолчанию Omit KeyPart1, когда помощник строит изтип.этот Неаточечная запись:это Волясодержит полеизточное имя,Это поле содержит вложенные поля, которые вы хотите исключить из исходного типасередина. Потому что это,Вы можете смело использовать существующую утилиту такого типа.

тытолькосуществоватьиспользовать Omit удалить T[KeyPart1] серединаиз Некоторые вложенные поля, для этого придется пересобирать T[KeyPart1] изтип.

Чтобы избежать перестройки всего T тип,тыиспользовать Omit только из T серединаудалить KeyPart1, сохраняя при этом другие поля. Тогда вам Волясуществовать следующую часть изтипсередина пересобрать T[KeyPart1]。

[KeyPart1 серединаизновыйключ]:NestedOmit<T[NewKeys], KeyPart2> датип карты, которому атрибут середина да может быть назначен KeyPart1 атрибут, что означает,что вы только что получили KeysToOmit серединаизвлекатьизчасть.

Это значит, что вы хотите удалить поле из родительского элемента. если ты пройдешь a.b.c, существование Когда вы впервые оцениваете свое состояние, это Воляда "a"серединаиз NewKeys。

а потомэтотсвойствоиз Тип установлен рекурсивный вызов NestedOmit Результаты типизации полезности, но сейчас существуютиспользовать T[NewKeys] Атрибут воляэтот изтипкак передается первый параметр типа T, и поскольку второй параметр типа передает остальные ключи в точечной записи, существуют KeyPart2 середина Можетиспользовать。

существоватьвнутреннийсостояниеиз false ветка середина, возвращение привязано к T изтекущийтип, как будто KeyPart1 Неа T Действительные ключи одинаковы:

Язык кода:javascript
копировать
type NestedOmit<T extends Record<string, any>, KeysToOmit extends string> =
  KeysToOmit extends `${infer KeyPart1}.${infer KeyPart2}`
    ?
      KeyPart1 extends keyof T
      ?
        Omit<T, KeyPart1>
        & {
          [NewKeys in KeyPart1]: NestedOmit<T[NewKeys], KeyPart2>
        }
      : T

Эта ветвь условия означает, что вы пытаетесь опустить T середина Не существуетсуществоватьиз Поле。существуют В этом случае,Нет необходимости идти дальше.

наконец,существоватьснаружиотделениесостояниеиз false ветвьсередина,использоватьсейчасиметьиз Omit Тип утилиты от Type серединаупущение KeysToOmit:

Язык кода:javascript
копировать
type NestedOmit<T extends Record<string, any>, KeysToOmit extends string> =
  KeysToOmit extends `${infer KeyPart1}.${infer KeyPart2}`
    ?
      KeyPart1 extends keyof T
      ?
        Omit<T, KeyPart1>
        & {
          [NewKeys in KeyPart1]: NestedOmit<T[NewKeys], KeyPart2>
        }
      : T
    : Omit<T, KeysToOmit>;

если условие KeysToOmit extends `{infer KeyPart1}.{infer KeyPart2}` ложно, что означает KeysToOmit Символа «использовать точку» нет, потому что его можно использовать уже существующий. Omit Тип служебной программы.

сейчассуществовать,хотетьиспользоватьновыйиз NestedOmit Тип условия, создайте файл с именем NestedObject изновыйтип:

Язык кода:javascript
копировать
type NestedObject = {
  a: {
    b: {
      c: number;
      d: number;
    };
    e: number;
  };
  f: number;
};

Затем вызовите NestedOmit, чтобы исключить вложенные поля, доступные в a.b.c:

Язык кода:javascript
копировать
type Result = NestedOmit<NestedObject, "a.b.c">;

существуют При оценке типа состояния впервые,Внешнее состояние Воля верное,Поскольку строковый литералтип“a.b.c”Можетназначен на Текст шаблонатип“{infer KeyPart1}.{infer KeyPart2}”。

существуют В данном случае KeyPart1 Воля понимается как строковый литерал типа «а», а KeyPart2 Воля подразумевается как оставшаяся часть строки из существующего, в данном случае середина — это «b.c».

сейчассуществовать Воля评估внутреннийсостояние。этот Воля Оценивается как истина,Потому что это KeyPart1 да T ключ. Ключевая часть1 сейчассуществоватьда“a”,и T Действительно существует атрибут «а»:

Язык кода:javascript
копировать
type NestedObject = {
a: {
b: {
c: number;
d: number;
};
e: number;
};
  f: number;
};

Продолжая оценивать обстановку, вы теперь внутри true внутри филиала. Этот Воля конструирует новый тип, который да является пересечением двух других типизов.

Первый тип типдасуществовать T начальствоиспользовать Omit Утилиты KeyPart1 из поля из результата, существование в данном случае середина равно a поле. Второй тип, который вы вызываете рекурсивно NestedOmit Тип сборки.

Если вы продолжите NestedOmit Из следующей оценки для первого рекурсивного вызова тип пересечения теперь является положительным и существует, создавая тип, который будет использоваться в качестве a Поле изтип. Эта воля воссоздает поле из без вложенных полей, которые необходимо пропустить.

существовать NestedOmit из Окончательная оценка середина, первое условие Воля возвращает false, поскольку при передаче строки тип существования появляется только «c». В этом случае вы можете опустить поле из объекта середина, используя встроенный помощник.

Это вернется b Поле изтип, т.е. опущено c изrawтип.Сейчас существует конец оценки, TypeScript возвращает то, что вы хотите.,и опустить вложенные поля.

в заключение

существовать Этот уроксередина,Мы исследуем дженерики, применимые к функциям, интерфейсам, классам и пользовательским,а такжеиспользовать Понятно Дженерики Приходитьсоздаватькартографированиетиписостояниетип.

Все это делает генерики вашим существованиеиспользовать. TypeScript В вашем распоряжении мощные инструменты. Правильное их использование Воля избавляет вас от повторения кода снова и снова и дает вам больше гибкости при написании изтипа.

Вышеуказанное — это весь контент, которым я поделюсь с вами сегодня. Надеюсь, этот контент будет вам полезен.

boy illustration
Учебное пособие по Jetpack Compose для начинающих, базовые элементы управления и макет
boy illustration
Код js веб-страницы, фон частицы, код спецэффектов
boy illustration
【новый! Суперподробное】Полное руководство по свойствам компонентов Figma.
boy illustration
🎉Обязательно к прочтению новичкам: полное руководство по написанию мини-программ WeChat с использованием программного обеспечения Cursor.
boy illustration
[Забавный проект Docker] VoceChat — еще одно приложение для мгновенного чата (IM)! Может быть встроен в любую веб-страницу!
boy illustration
Как реализовать переход по странице в HTML (html переходит на указанную страницу)
boy illustration
Как решить проблему зависания и низкой скорости при установке зависимостей с помощью npm. Существуют ли доступные источники npm, которые могут решить эту проблему?
boy illustration
Серия From Zero to Fun: Uni-App WeChat Payment Practice WeChat авторизует вход в систему и украшает страницу заказа, создает интерфейс заказа и инициирует запрос заказа
boy illustration
Серия uni-app: uni.navigateЧтобы передать скачок значения
boy illustration
Апплет WeChat настраивает верхнюю панель навигации и адаптируется к различным моделям.
boy illustration
JS-время конвертации
boy illustration
Обеспечьте бесперебойную работу ChromeDriver 125: советы по решению проблемы chromedriver.exe не найдены
boy illustration
Поле комментария, щелчок мышью, специальные эффекты, js-код
boy illustration
Объект массива перемещения объекта JS
boy illustration
Как открыть разрешение на позиционирование апплета WeChat_Как использовать WeChat для определения местонахождения друзей
boy illustration
Я даю вам два набора из 18 простых в использовании фонов холста Power BI, так что вам больше не придется возиться с цветами!
boy illustration
Получить текущее время в js_Как динамически отображать дату и время в js
boy illustration
Вам необходимо изучить сочетания клавиш vsCode для форматирования и организации кода, чтобы вам больше не приходилось настраивать формат вручную.
boy illustration
У ChatGPT большое обновление. Всего за 45 минут пресс-конференция показывает, что OpenAI сделал еще один шаг вперед.
boy illustration
Copilot облачной разработки — упрощение разработки
boy illustration
Микросборка xChatGPT с низким кодом, создание апплета чат-бота с искусственным интеллектом за пять шагов
boy illustration
CUDA Out of Memory: идеальное решение проблемы нехватки памяти CUDA
boy illustration
Анализ кластеризации отдельных ячеек, который должен освоить каждый&MarkerгенетическийВизуализация
boy illustration
vLLM: мощный инструмент для ускорения вывода ИИ
boy illustration
CodeGeeX: мощный инструмент генерации кода искусственного интеллекта, который можно использовать бесплатно в дополнение к второму пилоту.
boy illustration
Машинное обучение Реальный бой LightGBM + настройка параметров случайного поиска: точность 96,67%
boy illustration
Бесшовная интеграция, мгновенный интеллект [1]: платформа больших моделей Dify-LLM, интеграция без кодирования и встраивание в сторонние системы, более 42 тысяч звезд, чтобы стать свидетелями эксклюзивных интеллектуальных решений.
boy illustration
LM Studio для создания локальных больших моделей
boy illustration
Как определить количество слоев и нейронов скрытых слоев нейронной сети?
boy illustration
[Отслеживание целей] Подробное объяснение ByteTrack и детали кода