如何确保当我用
components: [1, 2]
注释掉第37行时,available: (c) => {
中的参数c不是never
而是获取类型number
?
主要问题是,如果我注释掉这一行,所有其他条目,即使它们具有不同的
components:[...]
,也会变成(c:never)=>{}
。
如果未提供
c
属性,我还希望方法中的参数 number
回退到类型 never
而不是 components
。
enum AAA {
a1 = 'a1', a2 = 'a2', a3 = 'a3',
}
enum BBB {
b1 = 'b1', b2 = 'b2', b3 = 'b3',
}
type TestData<T extends number = number> = {
components?: T[];
avaible: (c: T) => void;
}
// Adjust the RR type to allow partial records for both AAA and BBB
type RR = Partial<Record<AAA, Partial<Record<BBB, number>>>>;
type Param<T extends RR> = {
[KA in keyof T & AAA]?: {
[KB in keyof T[KA] & BBB]?: TestData<Extract<T[KA][KB], number>>
}
}
class Test<T extends RR> {
constructor(
public readonly data: Param<T>
) { }
}
// this is a db with literal entries
// i want pass to (c) type from .components or if not defined, juste use number
new Test(
{
[AAA.a1]: {
[BBB.b1]: {
components: [1, 2], // if i remove this, c should number and not never !?
avaible: (c) => {
// ^?
}
},
},
[AAA.a2]: {
[BBB.b2]: {
components: [5],
avaible: (c) => {
// ^?
}
},
[BBB.b1]: {
components: [8],
avaible: (c) => {
// ^?
}
},
},
}
);
看起来主要问题是,当
T
被constrained到RR
时,一旦你注释掉该行,推理就会失败,然后T
回落到RR
,此时你得到 Param<RR>
相当于 {[K in AAA]?: {}}
然后一切都停止工作。 我不是 100% 确定为什么会发生这种情况,但类型推断很棘手。
如果你移除约束,那么事情就会开始发生变化:
type Param<T> = {
[KA in keyof T]: {
[KB in keyof T[KA]]: (
TestData<Extract<T[KA][KB], number>>
)
}
}
class Test<T> {
constructor(
public readonly data: Param<T>
) { }
}
new Test(
{
[AAA.a1]: {
[BBB.b1]: {
avaible: (c) => {
// ^? (parameter) c: never
}
},
},
[AAA.a2]: {
[BBB.b2]: {
components: [5],
avaible: (c) => {
// ^? (parameter) c: 5
}
},
[BBB.b1]: {
components: [8],
avaible: (c) => {
// ^? (parameter) c: 8
}
},
},
}
);
现在仅推断出缺失的
components
never
。如果您希望它回落到 number
(这不是必需的,如果您愿意,您总是可以 注释 (c: number) => {}
),那么您可以使用额外的 条件类型:
type OrElse<T, D> = [T] extends [never] ? D : T;
type Param<T> = {
[KA in keyof T]: {
[KB in keyof T[KA]]: (
TestData<OrElse<Extract<T[KA][KB], number>, number>>
)
}
}
new Test(
{
[AAA.a1]: {
[BBB.b1]: {
avaible: (c) => {
// ^? (parameter) c: number
}
},
},
[AAA.a2]: {
[BBB.b2]: {
components: [5],
avaible: (c) => {
// ^? (parameter) c: 5
}
},
[BBB.b1]: {
components: [8],
avaible: (c) => {
// ^? (parameter) c: 8
}
},
},
}
);
看起来不错!
此时唯一的问题是
T
可以有各种键,而不仅仅是AAA
或BBB
:
new Test({x: {}}); // no error?!
您可以通过多种方式添加回约束,可能是将具有意外键的属性映射到
never
,例如:
type Param<T> = {
[KA in keyof T]: {
[KB in keyof T[KA]]: (
TestData<OrElse<Extract<T[KA][KB], number>, number>>
) & (KB extends BBB ? unknown : never)
} & (KA extends AAA ? unknown : never)
}
这会留下好的案例,但会给你带来错误
new Test({ x: {} }); // error!
我会说防止意外键/值的确切方法超出了这里的范围,要点是删除约束
extends RR
并在 Extract<T[KA][KB], number>
上使用条件类型以导致 never
成为number
。