在 TypeScript 中扩展联合类型别名? [英] Extending Union Types Alias in TypeScript?

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

问题描述

我试图在编译时将某些字符串字段限制为仅包含某些值.问题是这些值应该是可扩展的.这是一个简化的示例:

I'm trying to limit some string fields to be only of certain values at compile time. The problem is that those values should be extendable. Here's a simplified example:

type Foobar = 'FOO' | 'BAR';

interface SomeInterface<T extends Foobar> {
  amember: T;

  [key: string]: string; // this really has to stay
}

// let's test it

const yes = {
    amember: 'FOO'
} as SomeInterface<'FOO'>; // compiles as expected

//    const no = {
//        amember: 'BAZ'
//    } as SomeInterface<'BAZ'>; // Type '"BAZ"' does not satisfy the constraint 'Foobar' as expected

// so far so good
// Now the problem

abstract class SomeClass<T extends Foobar> {
  private anotherMember: SomeInterface<T>;
}

type Foobarbaz = Foobar | 'BAZ';

class FinalClass extends SomeClass<Foobarbaz> { //no good anymore
}

错误是

类型Foobarbaz"不满足约束Foobar".类型'"BAZ"' 不能分配给类型 'Foobar'.

Type 'Foobarbaz' does not satisfy the constraint 'Foobar'. Type '"BAZ"' is not assignable to type 'Foobar'.

所以问题是:如何在打字稿中将类型"限制为仅包含某些字符串,但可以扩展到其他字符串?或者这是一个 XY 问题并且有明显更好的解决方案?

So the question is: how in typescript can I limit a 'type' to be of certain strings only, but have it extendable with other strings? Or is this an XY problem and there's an obvious better solution?

Typescript 2.3.4 但我想如果有魔法的话我可以升级到 2.4.

Typescript 2.3.4 but i think i can upgrade to 2.4 if there's magic there.

推荐答案

我认为您使用的可扩展"一词与关键字 extends 的含义不同.通过说类型是可扩展的",您是说您希望能够扩大类型以接受更多值.但是当某些东西扩展一个类型时,这意味着你正在缩小类型以接受更少的值.

I think you're using the word "extendable" in a different sense from what the keyword extends means. By saying the type is "extendable" you're saying you'd like to be able to widen the type to accept more values. But when something extends a type, it means that you are narrowing the type to accept fewer values.

SomeInterface 本质上只能是以下四种类型之一:

SomeInterface<T extends Foobar> can essentially be only one of the following four types:

  • SomeInterface<'FOO'|'BAR'>: amember 可以是 'FOO''BAR'
  • SomeInterface<'FOO'>: amember 只能是 'FOO'
  • SomeInterface<'BAR'>:amember 只能是 'BAR'
  • SomeInterface:amember 不能取任何值
  • SomeInterface<'FOO'|'BAR'>: amember can be either 'FOO' or 'BAR'
  • SomeInterface<'FOO'>: amember can be only 'FOO'
  • SomeInterface<'BAR'>: amember can be only 'BAR'
  • SomeInterface<never>: amember cannot take any value

我有点怀疑这是否真的是你想要的,但只有你自己知道.

I kind of doubt that's actually what you want, but only you know that for sure.

另一方面,如果您希望 SomeInterface 被定义为 T 可以总是FOOBAR,但也可能是其他一些 string 值,你想要一些 TypeScript 没有完全提供的东西,这将指定 T.类似于 SomeInterface<TsuperFoobar extends string>,这是无效的 TypeScript.

On the other hand, if you want SomeInterface<T> to be defined such that T can always be either FOO or BAR, but also possibly some other string values, you want something that TypeScript doesn't exactly provide, which would be specifying a lower bound for T. Something like SomeInterface<TsuperFoobar extends string>, which isn't valid TypeScript.

但你可能只关心 amember 的类型,而不是 T.如果您希望 amember 成为 FOOBAR,但也可能是其他一些 string 值,您可以指定像这样:

But you probably only care about the type of amember, and not T. If you want amember to be either FOO or BAR, but also possibly some other string values, you could specify it like this:

interface SomeInterface<T extends string = never> {
  amember: Foobar | T;
  [key: string]: string; 
}

其中 T 只是您希望允许的额外文字的联合.如果您不想允许任何额外内容,请使用 never,或仅省略类型参数(因为我已将 never 设为默认值).

where T is just the union of extra literals you'd like to allow. If you don't want to allow any extra, use never, or just leave out the type parameter (since I've put never as the default).

让我们看看它的实际效果:

Let's see it in action:

const yes = {
    amember: 'FOO'
} as SomeInterface; // good, 'FOO' is a Foobar

const no = {
    amember: 'BAZ'
} as SomeInterface; // bad, 'BAZ' is not a Foobar

abstract class SomeClass<T extends string> {
  private anotherMember: SomeInterface<T>;
}

class FinalClass extends SomeClass<'BAZ'> { 
} // fine, we've added 'BAZ'

// let's make sure we did:
type JustChecking = FinalClass['anotherMember']['amember']
// JustChecking === 'BAZ' | 'FOO' | 'BAR'

<小时>

我回答你的问题了吗?希望有所帮助.


Did I answer your question? Hope that helps.

这篇关于在 TypeScript 中扩展联合类型别名?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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