泛型判别联合 [英] Discriminated Union of Generic type

查看:19
本文介绍了泛型判别联合的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我希望能够使用工会歧视 带有泛型.但是,它似乎不起作用:

I'd like to be able to use union discrimination with a generic. However, it doesn't seem to be working:

示例代码(在打字稿操场上查看):

interface Foo{
    type: 'foo';
    fooProp: string
}

interface Bar{
    type: 'bar'
    barProp: number
}

interface GenericThing<T> {
    item: T;
}


let func = (genericThing: GenericThing<Foo | Bar>) => {
    if (genericThing.item.type === 'foo') {

        genericThing.item.fooProp; // this works, but type of genericThing is still GenericThing<Foo | Bar>

        let fooThing = genericThing;
        fooThing.item.fooProp; //error!
    }
}

我希望打字稿能够认识到,因为我区分了通用的 item 属性,所以 genericThing 必须是 GenericThing.

I was hoping that typescript would recognize that since I discriminated on the generic item property, that genericThing must be GenericThing<Foo>.

我猜这只是不受支持?

另外,有点奇怪的是,在直接赋值之后,它 fooThing.item 失去了它的辨别力.

Also, kinda weird that after straight assignment, it fooThing.item loses it's discrimination.

推荐答案

问题

区分联合中的类型缩小受到若干限制:

The problem

Type narrowing in discriminated unions is subject to several restrictions:

没有展开泛型

首先,如果类型是泛型,则泛型不会被解包以缩小类型:缩小需要联合才能工作.因此,例如这不起作用:

Firstly, if the type is generic, the generic will not be unwrapped to narrow a type: narrowing needs a union to work. So, for example this does not work:

let func = (genericThing:  GenericThing<'foo' | 'bar'>) => {
    switch (genericThing.item) {
        case 'foo':
            genericThing; // still GenericThing<'foo' | 'bar'>
            break;
        case 'bar':
            genericThing; // still GenericThing<'foo' | 'bar'>
            break;
    }
}

虽然这样做:

let func = (genericThing: GenericThing<'foo'> | GenericThing<'bar'>) => {
    switch (genericThing.item) {
        case 'foo':
            genericThing; // now GenericThing<'foo'> !
            break;
        case 'bar':
            genericThing; // now  GenericThing<'bar'> !
            break;
    }
}

我怀疑解包具有联合类型参数的泛型类型会导致编译器团队无法以令人满意的方式解决的各种奇怪的极端情况.

I suspect unwrapping a generic type that has a union type argument would cause all sorts of strange corner cases that the compiler team can't resolve in a satisfactory way.

不会因嵌套属性而缩小

即使我们有类型的联合,如果我们测试嵌套属性,也不会发生缩小.一个字段类型可能会根据测试缩小,但不会缩小根对象:

Even if we have a union of types, no narrowing will occur if we test on a nested property. A field type may be narrowed based on the test, but the root object will not be narrowed:

let func = (genericThing: GenericThing<{ type: 'foo' }> | GenericThing<{ type: 'bar' }>) => {
    switch (genericThing.item.type) {
        case 'foo':
            genericThing; // still GenericThing<{ type: 'foo' }> | GenericThing<{ type: 'bar' }>)
            genericThing.item // but this is { type: 'foo' } !
            break;
        case 'bar':
            genericThing;  // still GenericThing<{ type: 'foo' }> | GenericThing<{ type: 'bar' }>)
            genericThing.item // but this is { type: 'bar' } !
            break;
    }
}

解决办法

解决方案是使用自定义类型保护.我们可以制作一个非常通用的类型保护版本,它适用于任何具有 type 字段的类型参数.不幸的是,我们不能为任何泛型类型创建它,因为它会绑定到 GenericThing:

The solution

The solution is to use a custom type guard. We can make a pretty generic version of the type guard that would work for any type parameter that has a type field. Unfortunately, we can't make it for any generic type since it will be tied to GenericThing:

function isOfType<T extends { type: any }, TValue extends string>(
  genericThing: GenericThing<T>,
  type: TValue
): genericThing is GenericThing<Extract<T, { type: TValue }>> {
  return genericThing.item.type === type;
}

let func = (genericThing: GenericThing<Foo | Bar>) => {
  if (isOfType(genericThing, "foo")) {
    genericThing.item.fooProp;

    let fooThing = genericThing;
    fooThing.item.fooProp;
  }
};

这篇关于泛型判别联合的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆