Generi c функция идентификации в Typescript - PullRequest
2 голосов
/ 14 апреля 2020

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

interface Options {
  a?: number;
  b?: number;
  c?: number;
}

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

const o: Options = { a: 1 } // Good: Object is type checked
o.a.toFixed(0)              // Bad: `a` is possibly undefined

Можно просто пропустить тип, но тогда нет проверки типа параметров:

const o = { a: 1, d: 2 } // Bad: `d` isn't an option
o.a.toFixed(0)           // Good: `a` is defined

Итак, я создал вспомогательную функцию:

const createOptions = <O extends Options>(options: O): O => options
const o = createOptions({ a: 1 }) // Good: Object is type checked
o.a.toFixed(0)                    // Good: `a` is defined

Это работает , но необходимость создавать такую ​​функцию для каждого типа опции становится раздражающей и грязной. Можно ли создать для этого единственную вспомогательную функцию generi c?


Моя первая попытка новичка заключалась в следующем, но здесь Typescript требует от меня предоставления 2 типов, а чем просто 1 (Options), который должен быть необходим.

const create = <T, U extends T>(obj: U): U => obj
const o = create<Options>({ a: 1 }) // Bad: Typescript wants me to specify U

Как я могу написать эту функцию тождества, поэтому мне нужно только указать T и иметь вывод Typescript U сам от объекта, в котором я прохожу?

Playground Link

1 Ответ

1 голос
/ 14 апреля 2020

Из-за отсутствия частичного вывода аргумента типа в настоящее время все или ничего: вы должны либо явно предоставить все аргументы типа, либо оставить задачу полностью для TS.

В любом случае для этого есть обходной путь:

function create<T extends object>() {
  return <U extends T>(obj:U) => obj
}
const o3 = create<Options>()({ a: 4 })

Это не очень красиво, но оно делает свое дело.

Как насчет части "Я не хочу, чтобы вещи, которые я не указал, были доступны"? Что касается точки зрения TS, такой объект, как { a: 4, d: "hi" } , расширяет (присваивается) до Options.

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

type ExtractImplementedKeys<ST, T> = {
  [K in Extract<keyof T, keyof ST>]: ST[K] 
}

function create<T extends object>() {
  return <U extends T>(obj:U): ExtractImplementedKeys<U, T> => obj
}

Вы по-прежнему сможете создавать объекты, например:

const o3 = create<Options>()({ a: 4, d:"Hi" })

Но тогда вы не сможете использовать лишние свойства:

o3.d; // <-- Error

Другая стратегия заключается в следующем:

type StrictExtendCheck<ST, T> = keyof ST extends keyof T ? ST : never

function create<T extends object>() {
  return <U extends T>(obj:StrictExtendCheck<U, T>) => obj
}

Затем что-то вроде:

const o3 = create<Options>()({ a: 4, d:"Hi" })

Возникнет ошибка, к сожалению, не очень описательная.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...