【问题标题】:How to combine index types with intersection types如何将索引类型与交集类型结合起来
【发布时间】:2020-05-19 01:04:56
【问题描述】:

我正在尝试定义一个接受如下剩余属性的类型:

export type Base = {
  numberProperty: number;
  booleanProperty: boolean;
};

export type Residual = {
  [key: string]: string;
};

export type Complete = Base & Residual;

const abc: Complete = {
  numberProperty: 1234,
  booleanProperty: true,
  residualProperty: 'abc',
};

换句话说,我想确保numberPropertybooleanProperty 始终分别为numberboolean 类型,但任何其他属性都应为string。但是,在编译此(3.9.2)时,出现以下错误:

error TS2322: Type '{ numberProperty: number; booleanProperty: true; residualProperty: string; }' is not assignable to type 'Complete'.
  Type '{ numberProperty: number; booleanProperty: true; residualProperty: string; }' is not assignable to type 'Residual'.
    Property 'numberProperty' is incompatible with index signature.
      Type 'number' is not assignable to type 'string'.

13 const abc: Complete = {
         ~~~


Found 1 error.

我发现了类似的问题和解决类似问题的文档,但我还没有找到关于如何执行此操作的结论性答案。

【问题讨论】:

  • 您的 Residual 使 base 中的任何定义完全无用,因为 numberPropertybooleanProperty 也是对象的键,也许您可​​以找到一种方法从 @987654332 中排除这些键@ 定义?虽然如果您可以简单地移动对象上定义其他属性的位置:)

标签: typescript


【解决方案1】:

问题是Residual 与来自Base 的键numberPropertybooleanProperty 有冲突,因为它们不是字符串。

要修复它,您需要更改 Residual 以表示除 Base 之外的所有内容都是字符串。之后Residual 尊重Base,它们可以组合在一起。

export type Base = {
  numberProperty: number;
  booleanProperty: boolean;
};

export type Residual = {
  [K in keyof any]: K extends keyof Base ? never : string;
};

export type Complete = Base | Residual;

const abc: Complete = {
  numberProperty: 1234,
  booleanProperty: true,
  residualProperty: 'abc',
};

如果您无法更改Residual,那么您应该重新考虑您的代码以避免它们的联合,因为它存在冲突并且无效。

const val1: Residual = {
  numberProperty: 'string', // valid, positive
};
const val2: Base = {
  numberProperty: 'string', // invalid, negative
};
const val3: Residual | Base = {
  numberProperty: 'string', // positive & negative = negative.
};


对于深度类型,我们必须省略 string 并始终使用定义的键进行操作。通常人们有 2 个选项 - 指定所有键或使用验证功能。

export type Base = {
  numberProperty: number;
  booleanProperty: boolean;
};

export type Residual<KEYS extends keyof any = keyof any> = {
  [K in KEYS]: string;
};

export type Complete<K extends keyof any> = Base & Residual<Exclude<K, keyof Base>>;

const abc: Complete<'residualProperty'> = {
  numberProperty: 1234,
  booleanProperty: true,
  residualProperty: 'abc',
};

const booleanVariable: boolean = abc.booleanProperty;


const validateComplete = <T extends Complete<K>, K extends keyof T>(value: T): Complete<K> => value;

const abc2 = validateComplete({
  numberProperty: 1234,
  booleanProperty: true,
  residualProperty: 'abc',
});

const booleanVariable2: boolean = abc2.booleanProperty;
const stringVariable2: string = abc2.residualProperty;

【讨论】:

  • 谢谢,这正是我想要的!
  • 实际上,建议的答案引入了使所有属性都成为可选字符串的副作用。在提议的更改中添加以下内容会触发另一个错误:``` const booleanFunc = (bool: boolean) => { return bool; } 常量 x = booleanFunc(abc.booleanProperty); ``` 错误:``` 类型参数 'string | boolean' 不能分配给'boolean' 类型的参数。类型“字符串”不可分配给类型“布尔”。ts(2345)```
  • 你把[K in keyof any]后面的问号去掉了吗?我认为这是昨天的最新变化。
  • [key: string]: string 正是这样做的——它说所有属性都是字符串。所有自定义字段都应该在Base 中定义,否则你应该删除Residual,因为它的签名强制所有内容都是字符串。
  • 是的,我相信我已经正确地复制了它。我对此没意见,尽管在期望Complete 类型的对象时,以某种方式保持Base 中定义的类型不受Residual 的影响会很棒。也许这将在未来的 TypeScript 版本中添加。
猜你喜欢
  • 2011-10-04
  • 2020-04-07
  • 1970-01-01
  • 2020-04-22
  • 2013-03-05
  • 2018-10-26
  • 2015-08-03
  • 1970-01-01
相关资源
最近更新 更多