我正在寻找一种可读的方式来在TypeScript中进行类型安全的转换。我的对象带有可区分的并集,并且我的转换语法不清楚。
enum my_type {
drag = 'drag',
std = 'std',
}
interface base {
type: my_type
}
interface drag {
type: my_type.drag
}
interface std {
type: my_type.std
}
type all = drag | std
function test_drag(obj: all) {
let obj_drag: drag|null = obj.type == my_type.drag ? obj : null
if (!obj_drag) {
console.log("It's not a drag")
return
}
console.log("It's a drag")
}
test_drag({type: my_type.drag}) // yes
test_drag({type: my_type.std}) // no
test_drag({type: 'drag'} as any) // yes
我对一种更清洁的方式感兴趣。带有 let obj_drag: drag|null = obj.type == my_type.drag ? obj : null
类型参数的泛型函数会很好,但是我不知道如何正确使用语法。也就是说,我想要一个看起来像这样的函数:
function dynamic_cast<DiscriminatingType>(obj): ObjectType
所以我可以这样称呼它:
let obj_drag = dynamic_cast<my_type.drag>(obj)
let obj_std = dynamic_cast<my_type.std>(obj)
这种功能是否可能或与其接近?主要是我想要可读的东西,最好不要同时指定判别式和结果类型。
@jcalz提供此通用方法。
function dynamicCastO<T, K extends keyof T, V extends T[K] & (string | number | boolean)>(
obj: T, k: K, v: V) {
return obj[k] === v ? obj as Extract<T, Record<K, V>> : null;
}
所以我想知道我是否可以消除通过的需要type
。在我下面的尝试中,返回类型不是所需的类型。
function dynamicCast<K, T extends { type: K }>(
obj: T, v: K) {
return obj.type === v ? obj : null;
}
编写可以区分已区分联合的函数的一种可能方式是:
const discriminateUnion = <K extends PropertyKey>(
discriminantKey: K
) => <T extends Record<K, string | number | boolean>, V extends T[K]>(
obj: T, discriminantValue: V
) => obj[discriminantKey] === discriminantValue ? obj as Extract<T, Record<K, V>> : null;
const dynamicCast = discriminateUnion("type");
这里discriminateUnion
采用与已区分联合的区分键相对应的单个参数。就您而言,是"type"
。然后,它返回另一个函数,该函数接受已区分联合类型的对象和要检查的判别值。如果对象的判别属性与该值匹配,则它将缩小的对象返回到并集的相关成员。否则返回null
。
判别式属性必须具有可比性===
,这样才能起作用,因此我将其限制为string | number | boolean
。如果您有其他类似的单位判别式null
,undefined
也可以添加它。返回类型Extract<T, Record<K, V>>
使用内置的实用程序类型来提取T
其key属性K
值为value的联合成员V
。这里可能会有一些极端情况;例如,如果您的判别式是可选的,则可能需要执行Extract<T, Partial<Record<K, V>>>
某些操作或使其起作用。
让我们尝试一下。我将更改类型以符合标准TypeScript命名约定(大写和驼峰式的东西),并向您的工会成员添加一些结构,以表明歧视产生了切实的结果:
enum MyType {
DRAG = 'drag',
STD = 'std',
}
interface Base {
type: MyType
}
interface Drag {
type: MyType.DRAG
dragProp: string;
}
interface Std {
type: MyType.STD
stdProp: string;
}
type All = Drag | Std
然后的实现testDrag()
将使用dynamicCast
如下形式:
function testDrag(obj: All) {
let objDrag = dynamicCast(obj, MyType.DRAG);
if (!objDrag) {
console.log("It's not a drag")
return
}
console.log("It's a drag, dragProp is " + objDrag.dragProp.toUpperCase())
}
而且您的测试行为符合预期:
testDrag({ type: MyType.DRAG, dragProp: "hello" }) // It's a drag, dragProp is HELLO
testDrag({ type: MyType.STD, stdProp: "goodbye" }) // It's not a drag
本文收集自互联网,转载请注明来源。
如有侵权,请联系 [email protected] 删除。
我来说两句