TypeScript中类型区分强制转换的类型安全转换(例如C ++的dynamic_cast)

edA-qa mort-ora-y

我正在寻找一种可读的方式来在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如果您有其他类似的单位判别式nullundefined也可以添加它。返回类型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] 删除。

编辑于
0

我来说两句

0 条评论
登录 后参与评论

相关文章

C ++中的dynamic_cast和static_cast

C ++中的复杂dynamic_cast

dynamic_cast将“ this”转换为派生类型:什么时候合法?

C ++类型比较:typeid与双调度dynamic_cast

dynamic_cast不适用于非多态类型的原因

为什么dynamic_cast可以用于非多态类型的上传?

std :: function中的参数自动dynamic_cast

不使用dynamic_cast <...>()如何知道具体的对象类型

动态强制转换(dynamic_cast)在OSx(XCode)上使用.dylib失败

为什么来自Objective-C ++的dynamic_cast在调试中成功但在发布中失败?

非多态类型上的Dynamic_cast

为什么dynamic_cast允许使用非唯一基类类型?

C ++入门是否对使用dynamic_cast产生了错误?

是否应该在每次向下转换时使用dynamic_cast?

比C ++中的dynamic_cast更好的解决方案

C ++ dynamic_cast问题

C ++,多重继承和dynamic_cast

未声明CLASS_NAME-无法dynamic_cast(目标不是指针或对完整类型的引用)

类型不完整的dynamic_cast问题

具有dynamic_cast和typeid的C ++多态

使用unique_ptr的多态类的C ++ static_cast和dynamic_cast

如何在没有dynamic_cast的情况下对派生类型实现“较少”?

dynamic_cast对于动态类型为强制类型的对象失败

使用dynamic_cast向下转换返回null

Dynamic_cast 未显示正确的对象类型

游戏引擎:dynamic_cast - 对象类不是多态类型

不能使用 dynamic_cast 从 Base 转换为 Derived

C++ 中 dynamic_cast 的问题

C++ 入门第 5 版:dynamic_cast