我正在尝试输入一个对象数组,其中每个对象都具有一组中的一个键。例如:
const foo = [
{ a: 'foo' },
{ b: 'bar' },
{ c: 'baz' },
]
Run Code Online (Sandbox Code Playgroud)
我的第一次尝试是key in联合:
type Foo = { [key in 'a' | 'b' | 'c']: string }[]
const foo: Foo = [
{ a: 'foo' },
{ b: 'bar' },
{ c: 'baz' },
]
Run Code Online (Sandbox Code Playgroud)
这不起作用,因为 Typescript 希望每个对象都拥有联合中的所有键:
type Foo = { [key in 'a' | 'b' | 'c']: string }[]
const foo: Foo = [
{ a: 'foo', b: 'bar', c: 'baz' },
{ a: 'foo', b: 'bar', c: 'baz' },
{ a: 'foo', b: 'bar', c: 'baz' },
]
Run Code Online (Sandbox Code Playgroud)
我的第二次尝试是:
type A = { a: string }
type B = { b: string }
type C = { c: string }
type Foo = (A | B | C)[]
const foo: Foo = [
{ a: 'foo' },
{ b: 'bar' },
{ c: 'baz' },
]
Run Code Online (Sandbox Code Playgroud)
const foo: Foo = [{ a: 'foo', b: 'bar' }]
Run Code Online (Sandbox Code Playgroud)
有没有一种方法可以强制每个对象都只有一个键,并且该键是a or b 或 c?
我们的项目正在尝试读取此 JSON,以处理 React 中不同国家/地区地址字段的动态表单。当 Typescript 读取 JSON blob 时,大多数情况都会出错。最重要的是,它认为fields密钥并不总是数组,因此不会让我.map忽略它。因此,我决定将 JSON blob 复制到我们的项目中并手动输入。我试图捕捉这样一个事实:该fields数组是一个对象数组,这些对象是thoroughfare、premise、 或 ,locality而这locality是一个对象数组localityname,这些对象是 等。
如果您想要一种只需要一个键的类型,则可以(大多数情况下)将其表示为对象类型的联合,其中联合的每个成员都定义了一个键,而所有其余键都是可选的且类型never为。(实际上,这也允许undefined,请参阅ms/TS#13195,除非您使用不属于该套件的编译器选项--exactOptionalPropertyTypes--strict。
所以你的Foo应该看起来像这样:
type Foo = Array<
{ a: string; b?: never; c?: never; } |
{ a?: never; b: string; c?: never; } |
{ a?: never; b?: never; c: string; }
>
Run Code Online (Sandbox Code Playgroud)
我们如何以编程方式获得它或类似的东西?解释起来有点棘手,但我的解决方案如下所示:
type ExactlyOneKey<K extends keyof any, V, KK extends keyof any = K> =
{ [P in K]: { [Q in P]: V } &
{ [Q in Exclude<KK, P>]?: never} extends infer O ?
{ [Q in keyof O]: O[Q] } : never
}[K];
type Foo = Array<ExactlyOneKey<"a" | "b" | "c", string>>;
Run Code Online (Sandbox Code Playgroud)
该类型ExactlyOneKey<K, V>采用键联合K并对其进行迭代。P对于联合的每个成员,它都会创建一个对象类型,其中存在该键,而其他键不存在/丢失。类型{[Q in P]: V}(又名Record<P, V>)具有当前的键和值,并且该类型{[Q in Exclude<KK, P>]?: never}具有所有其余键作为可选且从不。我们将它们与 相交&以获得具有这两种特征的类型。然后我做了一个小技巧,... extends infer O ? { [Q in keyof O]: O[Q] } : never将获取该类型...并将所有交叉点合并为单个对象类型。这并不是绝对必要的,但它会改变{a: string} & {b?: never, c?: never}为{a: string; b?: never; c?: never;}更容易接受的。
让我们确保它有效:
const foo: Foo = [
{ a: 'foo' },
{ b: 'bar' },
{ c: 'baz' },
]; // okay
const badFoo: Foo = [
{ d: "nope" }, // error
{ a: "okay", b: "oops" } // error
];
Run Code Online (Sandbox Code Playgroud)
看起来不错。
| 归档时间: |
|
| 查看次数: |
2912 次 |
| 最近记录: |