【问题标题】:typescript: enforcing existing object keys打字稿:强制执行现有的对象键
【发布时间】:2020-05-31 00:19:13
【问题描述】:

我希望dropdownAttributes 仅限于DropDownItem 接口上的属性。

interface DropDownItem {
    [key: string]: any;
}
interface Props {
   dropdownList: DropDownItem[];
   dropdownAttributes: string[];
}

如果DropDownItem 现在确实有动态属性,我想我可以像这样用keyof 解决这个问题:

interface Props {
   dropdownList: DropDownItem[];
   dropdownAttributes: (keyof DropDownItem)[];
}

但这在我的情况下现在不起作用。如何解决?

【问题讨论】:

  • 我认为你不能再锁定它了。 DropDownItem 允许任何字符串键保存任何值,因此在 dropdownAttributes 中包含字符串是尽可能紧凑的。正如你所说,如果DropDownItem 没有索引签名,你可以进一步限制dropdownAttributes,但就这样,你不能。 (在 TypeScript 级别。)
  • 但是DropDownItem 有一个索引签名,所以它可以有任何键,所以keyof DropDownItem 只是string(好吧string | number 但那是另一回事)。如果密钥未知,TS 无法帮助您在编译时强制执行它们。

标签: typescript keyof


【解决方案1】:

如果在接口中将键定义为[key: string]: value,则不能提供keyof,因为这意味着几乎可以有任何键。

因此,这个keyof DropDownItem 代码也返回string | number,因为这些是key 可以拥有的值。

您可以通过为对象接口定义特定键来避免这种情况:

interface DropdownItem {
   id: number,
   text: string,
   isDisplayed: boolean,
}

interface Props {
   dropdownList: DropdownItem[],
   dropdownAttributes: (keyof DropdownItem)[] // ("id" | "text" | "isDisplayed")[]
}

【讨论】:

  • 我希望类型是string。为什么是string | number
  • @ShamPooSham 这有点技术性,但 JavaScript 将对象属性视为字符串之间的联合类型 |数字和文字(类似字符串或类似符号)。这基本上意味着索引为const obj = { 1: 'one' } 的对象会自动强制转换为const obj = { "1": "one"},但由于 TS 只是一个“在 JS 上运行”的超集,它仍然需要继承属性可以键入为的 JS 行为number 运行前,尽管它会在编译期间被强制转换为 string
  • 后续 ^:在 SO 上也有关于此的高质量答案,其中更详细地介绍了 here。我绝对建议您阅读,因为为了评论的可读性,我对我的评论进行了相当多的简化
【解决方案2】:

您似乎希望Props 是通用的,以便它可以被不同的对象类型使用。这可以通过在Props 中定义一个泛型类型T 来实现

interface Props<T> {
   dropdownList: T[];
   dropdownAttributes: (keyof T)[];
}

现在,如果我们提前知道某个对象的类型,我们可以为其创建一个接口,并在Prop 中创建一个使用该接口的类型

interface MyDropDownItem {
  foo : number
}

type MyDropDownItemProps = Props<MyDropDownItem>;

我们现在只能在dropdownList 中使用MyDropDownItem 的实例及其在dropdownAttributes 中的键

const good: MyDropDownItemProps = {
  dropdownList: [{foo: 2}],
  dropdownAttributes : ['foo']
}

const bad: MyDropDownItemProps = {
  dropdownList: [{foo: 2, bar: 's' /* error here */}],
  dropdownAttributes : ['foo', 'bar' /* and here */ ]
}

这当然假设您事先知道下拉菜单的结构,因为这是 typescript 唯一可以帮助您的事情。 Typescript 不会帮助您实现运行时类型安全。

Check it out on stackblitz

【讨论】:

    【解决方案3】:

    最后我做到了。

    interface Props<T> {
       dropdownList: T[];
       dropdownAttributes: (keyof T)[];
    }
    
    declare class MyComponent<T> extends React.Component<Props<T>> {}
    
    export default MyComponent;
    
    

    用法:

    interface DropdownItem {
       key1: string;
       key2: string;
    }
    
    <MyComponent
       <DropdownItem>
       dropdownAttributes={['key1', 'key2']}
       dropdownList={[{key1: 'hello', key2: 'world'}]}       
    />
    
    

    【讨论】:

      猜你喜欢
      • 2020-04-09
      • 2017-09-29
      • 2019-10-31
      • 2016-09-20
      • 2013-09-01
      • 1970-01-01
      • 2021-06-28
      • 2021-09-15
      • 2020-04-20
      相关资源
      最近更新 更多