如何基于对象内部的值定义文字类型

时间:2019-01-25 08:24:55

标签: javascript reactjs typescript types flowtype

在流程中,我可以这样定义一个动态文字类型:

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; }'.

编辑:

所以我得到了它的工作,正是我希望它如何工作:

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'部分?

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

,然后输入为

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

它完全按照我想要的方式工作,只是想知道我是否可以在这里删除重复项(因为prop: 'fontSize'已经定义了密钥)。这就引出了我最初的问题,即如何在此处定义值fontSize作为类型内的键。

2 个答案:

答案 0 :(得分:0)

当前这是不可能的,因为您要部分指定和部分推断类型参数。一旦this 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],
  }
})

答案 1 :(得分:0)

如果没有部分参数类型推断,则可以将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],
    }
})

Playground