Jac*_*pie 6 typescript conditional-types
我正在寻找一种为以下对象创建TypeScript类型的方法,该对象具有两个已知键和一个具有已知类型的未知键:
interface ComboObject {
known: boolean
field: number
[U: string]: string
}
const comboObject: ComboObject = {
known: true
field: 123
unknownName: 'value'
}
Run Code Online (Sandbox Code Playgroud)
该代码无效,因为TypeScript要求所有属性都与给定索引签名的类型匹配。但是,我不想使用索引签名,我想在一个我知道其类型但不知道其名称的地方键入一个字段。
到目前为止,我唯一的解决方案是使用索引签名并设置所有可能类型的联合类型:
interface ComboObject {
[U: string]: boolean | number | string
}
Run Code Online (Sandbox Code Playgroud)
但这有很多缺点,包括允许在已知字段上输入不正确的类型以及允许任意数量的未知键。
有没有更好的方法?使用TypeScript 2.8条件类型有帮助吗?
bra*_*ipt 11
在我看来,更简单的方法是使用交叉类型:
type ComboObject = {
known: boolean
field: number
} & {
[key: string]: string | number | boolean;
};
Run Code Online (Sandbox Code Playgroud)
这告诉 TypeScript 从左侧继承,但当你向它提供额外的未知参数类型时,它不会生气。
一个稍微冗长和复杂的解决方案,但可以通过显式Exclude调用已知类型来提供一些额外的约束:
type ComboObject = {
known: boolean;
field: number;
} & Record<Exclude<string, "known" | "field">, string | number | boolean>;
Run Code Online (Sandbox Code Playgroud)
type ComboObject = {
known: boolean
field: number
} & {
[key: string]: string | number | boolean;
};
Run Code Online (Sandbox Code Playgroud)
让我们做一些类型操作来检测给定类型是否为并集。它的工作方式是使用条件类型的分布属性将并集散布到组成部分,然后注意每个组成部分都比并集窄。如果那不是真的,那是因为工会只有一个组成部分(所以它不是工会):
type IsAUnion<T, Y = true, N = false, U = T> = U extends any
? ([T] extends [U] ? N : Y)
: never;
Run Code Online (Sandbox Code Playgroud)
然后使用它来检测给定string类型是否为单个字符串文字(因此:not string,not never和not union):
type IsASingleStringLiteral<
T extends string,
Y = true,
N = false
> = string extends T ? N : [T] extends [never] ? N : IsAUnion<T, N, Y>;
Run Code Online (Sandbox Code Playgroud)
现在,我们可以开始处理您的特定问题。定义BaseObject为ComboObject您可以直接定义的一部分:
type BaseObject = { known: boolean, field: number };
Run Code Online (Sandbox Code Playgroud)
为准备错误消息,我们定义一个,ProperComboObject以便当您搞砸时,错误提示您应该执行的操作:
interface ProperComboObject extends BaseObject {
'!!!ExactlyOneOtherStringPropertyNoMoreNoLess!!!': string
}
Run Code Online (Sandbox Code Playgroud)
这是主要课程。 VerifyComboObject<C>接受一个类型C,如果它符合您想要的ComboObject类型,则返回原状;否则,它将返回ProperComboObject(也将不符合)错误。
type VerifyComboObject<
C,
X extends string = Extract<Exclude<keyof C, keyof BaseObject>, string>
> = C extends BaseObject & Record<X, string>
? IsASingleStringLiteral<X, C, ProperComboObject>
: ProperComboObject;
Run Code Online (Sandbox Code Playgroud)
它通过解剖C成BaseObject,其余的按键X。如果C不匹配BaseObject & Record<X, string>,则您失败了,因为这意味着它不是BaseObject,或者是具有额外非string属性的一个。然后,通过与进行检查,确保仅剩下一个密钥。 XIsASingleStringLiteral<X>
现在我们创建一个帮助函数,该函数要求输入参数match VerifyComboObject<C>,并返回不变的输入。如果您只想要正确类型的对象,它可以让您尽早发现错误。或者,您可以使用签名来帮助使自己的功能需要正确的类型:
const asComboObject = <C>(x: C & VerifyComboObject<C>): C => x;
Run Code Online (Sandbox Code Playgroud)
让我们测试一下:
const okayComboObject = asComboObject({
known: true,
field: 123,
unknownName: 'value'
}); // okay
const wrongExtraKey = asComboObject({
known: true,
field: 123,
unknownName: 3
}); // error, '!!!ExactlyOneOtherStringPropertyNoMoreNoLess!!!' is missing
const missingExtraKey = asComboObject({
known: true,
field: 123
}); // error, '!!!ExactlyOneOtherStringPropertyNoMoreNoLess!!!' is missing
const tooManyExtraKeys = asComboObject({
known: true,
field: 123,
unknownName: 'value',
anAdditionalName: 'value'
}); // error, '!!!ExactlyOneOtherStringPropertyNoMoreNoLess!!!' is missing
Run Code Online (Sandbox Code Playgroud)
第一个根据需要进行编译。最后三个失败的原因与额外属性的数量和类型有关。该错误消息有点神秘,但这是我能做的最好的事情。
您可以在Playground中查看运行中的代码。
同样,我不建议将其用于生产代码。我喜欢使用类型系统,但是这种系统特别复杂且脆弱,我也不想为任何无法预料的后果负责。
希望对您有帮助。祝好运!
不错的@jcalz
它给了我一些很好的洞察力,让我能够到达我想要的地方。我有一个具有一些已知属性的 BaseObject,并且 BaseObject 可以拥有任意数量的 BaseObject。
type BaseObject = { known: boolean, field: number };
type CoolType<C, X extends string | number | symbol = Exclude<keyof C, keyof BaseObject>> = BaseObject & Record<X, BaseObject>;
const asComboObject = <C>(x: C & CoolType<C>): C => x;
const tooManyExtraKeys = asComboObject({
known: true,
field: 123,
unknownName: {
known: false,
field: 333
},
anAdditionalName: {
known: true,
field: 444
},
});
Run Code Online (Sandbox Code Playgroud)
这样我就可以对已有的结构进行类型检查,而无需进行太多更改。
蒂
| 归档时间: |
|
| 查看次数: |
3078 次 |
| 最近记录: |