我正在try 创建一种在不同阶段逐步填充的类型.例如,在创建一个Bar对象时,我希望Data<DataType.bar>包含一个完整的Foo对象,但Bar本身和Baz都不是Data的一部分:

enum DataType {
  'foo',
  'bar',
  'baz',
}

type Foo = {
  a: string;
};
type Bar = {
  b: string;
};
type Baz = {
  c: string;
};

type Data<T extends DataType | null = null> = {
  [DataType.foo]: T extends DataType.bar | DataType.baz | null ? Foo : never;
  [DataType.bar]: T extends DataType.baz | null ? Bar : never;
  [DataType.baz]: T extends null ? Baz : never;
};

const myData: Data = {
  [DataType.foo]: { a: 'I should already exist while bar is being created' },
  /*
  [DataType.bar]: {
     b: `I correctly error when uncommented...
         but when commented, DataType.bar also appears to be required?`
  }
  */
};

尽管当包含DataType.bar时,myData正确地包含了一个错误,但当它被注释掉时,我也会得到以下错误:

Type '{ 0: { a: string; }; }' is missing the following properties from type 'Data<DataType.bar>': [DataType.bar], [DataType.baz]

有没有办法告诉TypeScrip,基于T extends DataType个泛型,这些属性实际上并没有丢失?

EDIT:这似乎是我想要的,然而它看起来真的重复了吗?有没有更好的方法来做这件事?

type Data<T extends DataType | null = null> = T extends null
  ? {
      [DataType.foo]: Foo;
      [DataType.bar]: Bar;
      [DataType.baz]: Baz;
    }
  : T extends DataType.baz
  ? {
      [DataType.foo]: Foo;
      [DataType.bar]: Bar;
    }
  : T extends DataType.bar
  ? {
      [DataType.foo]: Foo;
    }
  : T extends DataType.foo
  ? {}
  : never;

推荐答案

使用interfaceextends

enum DataType {
  'foo',
  'bar',
  'baz',
}
interface DataFoo { [x:string]: never}
interface DataBar { foo: string; }; 
interface DataBaz extends DataBar { bar: string; };
interface DataAll extends DataBaz { baz: string; };
type Data<T extends DataType | null = null> = T extends null ? DataAll
  : T extends DataType.baz
  ? DataBaz
  : T extends DataType.bar
  ? DataBar
  : T extends DataType.foo
  ? DataFoo
  : never;
const dataFoo: Data<DataType.foo> = {};
const dataBar: Data<DataType.bar> = { foo: "" };
const dataBaz: Data<DataType.baz> = { foo: "", bar: "" };
const dataAll: Data = { foo: "", bar: "", baz:"" };

(见TypeScriptPlayground)

使用type和类型运算符&

enum DataType {
  'foo',
  'bar',
  'baz',
}
type DataFoo = Record<string,never>;
type DataBar = {foo:string};
type DataBaz = DataBar & {bar:string};
type DataAll = DataBaz & {baz:string};
type Data<T extends DataType | null = null> = T extends null ? DataAll
  : T extends DataType.baz
  ? DataBaz
  : T extends DataType.bar
  ? DataBar
  : T extends DataType.foo
  ? DataFoo
  : never;
const dataFoo: Data<DataType.foo> = {};
const dataBar: Data<DataType.bar> = { foo: "" };
const dataBaz: Data<DataType.baz> = { foo: "", bar: "" };
const dataAll: Data = { foo: "", bar: "", baz:"" };
    

(见TypeScriptPlayground)

Record是一个打字实用程序函数.Record<string,never>等同于{[x:string]:never}

主要好处是文本长度增加为O(Nlevels).在您的原始版本中,它增加为O(Nlevels^2).

Typescript相关问答推荐

TypScript ' NoInfer '类型未按预期工作

在TypeScript中,除了映射类型之外还使用的`in`二进制运算符?

将对象属性转换为InstanceType或原样的强类型方法

更新为Angular 17后的可选链运算符&?&问题

我可以为情态车使用棱角形的路由守卫吗?

TypeScrip-将<;A、B、C和>之一转换为联合A|B|C

TypeScrip界面属性依赖于以前的属性-针对多种可能情况的简明解决方案

如何解决类型T不能用于索引类型{ A:typeof A; B:typeof B;. }'

被推断为原始类型的交集的扩充类型的交集

一个打字类型可以实现一个接口吗?

如何创建一家Reduxstore ?

TypeScrip中的类型安全包装类

将对象的属性转换为正确类型和位置的函数参数

我怎样才能正确地输入这个函数,使它在没有提供正确的参数时出错

使用强类型元组实现[Symbol.iterator]的类型脚本?

是否使用类型将方法动态添加到类?

为什么我无法在 TypeScript 中重新分配函数?

React 中如何比较两个对象数组并获取不同的值?

错误:仅允许在‘使用服务器’文件中导出异步函数

如何将参数传递给条件函数类型