Как сделать вывод для TypeScript, что свойство может быть только одного типа на основе отфильтрованных ключей? - PullRequest
0 голосов
/ 06 апреля 2020

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

В следующем случае все работает хорошо, кроме как внутри тела функции. Строка return prop.substr(0) + payload содержит ошибки, потому что TypeScript не может сделать вывод, что state[property] может когда-либо ссылаться только на свойство, имеющее тип string.

type StringKeys<T> = { [K in keyof T]: T[K] extends string ? K : never }[keyof T]

const state = { one: 'string', two: 34, three: true }

type StateStringKeys = StringKeys<typeof state> // ✓ should only contain 'one'

const combineStrings = <S, P extends StringKeys<S>>(state: S, property: P, payload: S[P]): string => {
  const prop = state[property]
  return prop.substr(0) + payload // X - How do I tell TS that this could only ever be a string by now?
}
combineStrings(state, 'one', 'test') // ✓ should NOT error, 'one' is a string and a string is provided
combineStrings(state, 'one', 23) // ✓ should error, 'one' is a string but a number is provided
combineStrings(state, 'two', 23) // ✓ should error, 'two' is a number
combineStrings(state, 'three', 'test') // ✓ should error, 'three' is a boolean
combineStrings(state, 'four', false) // ✓ should error, 'four' does not exist

Есть ли в этом способе иметь тот же тип безопасность при вызове функции, но также выведите правильный тип string для prop.

Вот ссылка на TypeScript Playground , если хотите.

1 Ответ

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

Исправление состоит в том, чтобы отменить ограничение на уровне типа, заставить объект расширять объект строковыми значениями. Рассмотрим:

const combineStrings =
  < S extends Record<P, string> // here we say that object has P property as string
  , P extends keyof S >(state: S, property: P, payload: S[P]): string => {
  const prop = state[property]
  return prop.substr(0) + payload // works ?
}

Детская площадка

...