как определить литеральный тип на основе значения внутри объекта - PullRequest
0 голосов
/ 25 января 2019

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

const myVar = 'foo'

type X = {
  [typeof myVar]: string
}

const myX: X = { foo: 1 } // will throw, because number
const myX: X = { foo: 'bar' } // will not throw

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

type Theme = {
  fontSizes: number[]
}

type SystemObj = {
  prop: string,
  cssProperty?: string,
}

type Props<T> = T & {
  theme: Theme,
}

const style = <X>({
  prop,
  cssProperty,
}: SystemObj) => {
  const cssProp = cssProperty || prop

  return (props: Props<{
    [typeof cssProp]: X
  }>) => {
    return props
  }
}

const fontSize = style<number>({
  prop: 'fontSize',
})

fontSize({
  fontSize: 2,
  theme: {
    fontSizes: [12, 14, 16],
  }
})

В настоящее время выбрасывает (со всеми опциями, включенными на игровой площадке)

Argument of type '{ fontSize: number; theme: { fontSizes: number[]; }; }' is not assignable to parameter of type '{ theme: Theme; }'.
  Object literal may only specify known properties, and 'fontSize' does not exist in type '{ theme: Theme; }'.

EDIT:

Итак, я заставил его работать, именно так, как я хочу, чтобы он работал:

type Theme = {
  fontSizes: number[]
}

type SystemObj = {
  prop: string,
  cssProperty?: string,
}

type Props = {
  theme: Theme,
}

const style = <X extends string, Y>({
  prop,
  cssProperty,
}: SystemObj) => {
  const cssProp = cssProperty || prop

  return (props: Props & { [K in X]: Y }) => {
    return props
  }
}

const fontSize = style<'fontSize', number>({
  prop: 'fontSize',
})

fontSize({
  fontSize: 123,
  theme: {
    fontSizes: [12, 14, 16],
  }
})

Можно ли здесь избавиться от части <'fontSize'? * 1018

const fontSize = style<'fontSize', number>({
  prop: 'fontSize',
})

и просто введите это как

const fontSize = style<number>({
  prop: 'fontSize',
})

работает именно так, как я хочу, чтобы он работал, просто интересно, смогу ли я удалить здесь дублирование (потому что prop: 'fontSize' уже определяет ключ). Это приводит к моему первоначальному вопросу, как я могу определить значение fontSize здесь как ключ внутри моего типа.

Ответы [ 2 ]

0 голосов
/ 26 января 2019

Без частичного вывода типа аргумента вы можете разделить функцию style на «две части» - style вернет функцию, принимающую SystemObj. Таким образом, вы сможете указать тип значения (number), а имя реквизита будет выведено из SystemObj:

type SystemObj<TProp extends string> = {
    prop: TProp,
    cssProperty?: string,
}

const style = <TValue>() =>
    <TKey extends string>(systemObj: SystemObj<TKey>) =>
        (props: Props & { [K in TKey]: TValue }) => props

const fontSize = style<number>()({
    prop: 'fontSize',
})

fontSize({
    fontSize: 123,
    theme: {
        fontSizes: [12, 14, 16],
    }
})

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

0 голосов
/ 25 января 2019

В данный момент это невозможно, потому что вы хотите частично указать и частично вывести аргументы типа.Как только этот PR попадет в TypeScript, вы сможете сделать что-то вроде этого:

type SystemObj<T extends string> = { // <------ note T here
  prop: T,
  cssProperty?: string,
}

type Props = {
  theme: Theme,
}

const style = <X extends string, Y>({
  prop,
  cssProperty,
}: SystemObj<X>) => { // <---------------------- X as an argument here
  const cssProp = cssProperty || prop

  return (props: Props & { [K in X]: Y }) => {
    return props
  }
}

const fontSize = style<_, number>({ // <-------- Ask TS to infer type param
  prop: 'fontSize',
})

fontSize({
  fontSize: 123,
  theme: {
    fontSizes: [12, 14, 16],
  }
})
...