true & false - 给你never。类型不能是true 和false。想象一下可以是0 和同时是1 的数字。这是不可能的。
StateList 类型不应是联合类型。它应该是条件类型。您应该将其视为一个检查列表是否同时包含true 和false 的函数。
此外,StateType 类型允许您创建无效状态 StateType<true, "is false">,这并不酷。
如果我是你,我会创建一个BooleanMap:
type BooleanMap = {
true: true,
false: false
}
现在,我们可以遍历地图的每个键并创建适当的状态。
type Values<T> = T[keyof T]
type BooleanState<
T extends Record<string, boolean>
> = Values<{
[Prop in keyof T]: {
bool: T[Prop],
log: `is ${Prop & string}`
}
}>
// type State = {
// bool: true;
// log: "is true";
// } | {
// bool: false;
// log: "is false";
// }
type State = BooleanState<BooleanMap>
结果和你的一样。
好的,我知道这是一个有点冗长的解决方案。只是想向您展示这种技术,因为有时使非法状态无法表示非常有用。我在几个答案中使用了这种模式。见this
这个解决方案不那么冗长,并且使用了distributive-conditional-types 的力量:
type MakeState<Bool extends boolean> =
Bool extends true
? { bool: true, log: 'is true' }
: { bool: false, log: 'is false' }
type State = MakeState<boolean>
如果你想表达false & true === 'both',你不应该把它作为联合的一部分。应该在验证函数/类型中完成。
使用StateType<true & false, "both">[] 作为列表状态的一部分有点棘手,因为这种类型应该代表我们验证函数的结果。我们应该经过一些计算得到它。如果不使用条件类型,这种类型不能在 TypeScript 中自我表示。
为了验证state_list,我们需要额外的功能。
让我们编写一个函数并尝试推断参数的文字类型:
const validation = <
Elem extends State,
Tuple extends Elem[]
>(tuple: [...Tuple]) => tuple
validation([
{ bool: true, log: "is true" },
{ bool: true, log: "is true" },
])
如果您将鼠标悬停在validation 上,您将看到提供的参数已被推断出来。如果您对推理感兴趣,可以查看我的article。如果您想知道 [...Tuple] 的语法是什么意思 - 它是 variadic tuples。
好的,让我们继续。现在,当我们推断出元组中的每个元素时,我们可以编写Validate 类型。如果您对 typescript 中函数参数的类型验证感兴趣,可以查看我的文章:this 和 this。
type TupleMap<T extends State[],Result extends any[]=[]>=
T extends []
? Result
: T extends [infer Head,...infer Tail]
? Head extends State
? Tail extends State[]
? TupleMap<Tail,[...Result,{bool:Head['bool'],log:'both'}]>
: never
:never
:never
type Validation<Tuple extends any[]> =
boolean extends Tuple[number]['bool']
? TupleMap<Tuple>
: Tuple
Validation 需要 State 的元组。 boolean extends Tuple[number]['bool'] - 表示如果元组中每个元素的属性bool 是true | false 的联合,我们需要删除log 属性并添加另一个具有“both”值的属性。为了做到这一点,我们需要递归地遍历元组中的每个元素并覆盖log 属性。 Here、here 和 here 你会发现更多关于元组迭代的解释。
让我们在函数内部使用Validation:
type MakeState<Bool extends boolean> =
Bool extends true
? { bool: true, log: 'is true' }
: { bool: false, log: 'is false' }
type State = MakeState<boolean>
type EdgeCaseState = { bool: boolean, log: 'both' }
type TupleMap<T extends State[],Result extends any[]=[]>=
T extends []
? Result
: T extends [infer Head,...infer Tail]
? Head extends State
? Tail extends State[]
? TupleMap<Tail,[...Result,{bool:Head['bool'],log:'both'}]>
: never
:never
:never
type Validation<Tuple extends any[]> =
boolean extends Tuple[number]['bool']
? TupleMap<Tuple>
: Tuple
const validation = <
Elem extends State,
Tuple extends Elem[]
>(tuple: Validation<[...Tuple]>) => tuple
validation([
{ bool: true, log: "is true" },
{ bool: true, log: "is true" },
]) // no errors
validation([
{ bool: false, log: "is false" }, // expected error
{ bool: true, log: "is true" }, // expected error
])
validation([
{ bool: false, log: "both" }, // ok
{ bool: true, log: "both" }, // ok
])
Playground
TupleMap 是一个有点冗长的实用程序。可以重构它并使用更简洁的版本:
type TupleMap<T extends State[]> = {
[Prop in keyof T]: T[Prop] extends State ? { bool: T[Prop]['bool'], log: 'both' } : never
}
我知道你要问什么:
问:是否可以去掉多余的功能?
答:没有。请参阅this 文章。我们需要额外的函数来进行验证。
问:额外的函数会影响代码性能吗?
答:请看this的回答。