首页 > 解决方案 > 我如何表达一个参数可以扩展打字稿中的字符串列表?

问题描述

我有一个带string参数的方法。但是,我想稍微限制该参数。所以,我用我想接受的字符串创建了一个联合类型:

type Foo = 'a' | 'b';

现在,我有一个简单的方法:

function bar(foo: Foo) {
    // do something with that string
}

我想要做的是允许调用者传递其他字符串(除了aand b)。但是,我希望他们能够扩展原始联合类型。

function bar<T extends Foo>(str: T) {
    // do something with that string
}
type Bar = Foo | 'c';
// this doesn't work: Argument of type 'Bar' is not assignable to parameter of type 'Foo'. Type '"c"' is not assignable to type 'Foo'.
bar('c' as Bar); 

有没有办法在 TypeScript 中表达这个约束?

标签: typescript

解决方案


@jscalz 的评论让我重新考虑我的答案。

从经典的 OOP 角度来看,对泛型参数的extends约束bar<T extends Foo>(arg: T)将意味着T拥有 的所有属性Foo 以及更多属性。因此,有了这个约束,您可以放心地假设您bararg行为至少相似Foo(并且具有相同的属性结构)。

但是,在您的情况下,T它既不是 a 也不是classaninterface而是字符串文字的联合,这就是extends变得毫无意义的地方:

让我们假设按照您的bar<T extends Foo>(arg: T)意愿工作(如您的 OP 中所述),意思是,TFoo. 这意味着在你的bar()函数中你根本无法控制哪些值arg持有——它可能持有任何类型的值,你也可以使用any

鉴于上述情况,您控制其进入方式的唯一机会bar()是使用function bar(args: Bar)Bar = Foo | 'c'这也允许您在bar('c');不强制转换的情况下调用。


原答案:


定义type Bar交集类型似乎至少驯服了打字稿 linting 错误:type Bar = Foo & 'c';.

下面我列出了 typescript 规范中的相关部分(至少在我的理解中),这些部分定义了类型的交集实际上导致了extends约束识别的“子类型-超类型”关系:

3.11.3 子类型和超类型中它说:

S 是类型 T 的子类型,并且 T 是 S 的超类型,如果 S 相对于 T (3.11.5) 没有多余的属性,并且以下条件之一为真:

[...]

  • S 是联合类型,S 的每个组成类型都是 T 的子类型。

此外,在3.11.4 分配兼容性中(由我加粗):

S可分配给类型 T,并且 T 可从 S 分配,如果 S 相对于 T (3.11.5) 没有多余的属性,并且以下条件之一为真:

[...]

  • S 是一个联合类型,并且 S 的每个组成类型都可以分配给 T。

关于extends关键字,Typescript 手册指出以下内容(由我加粗):

出于实际目的,类型兼容性由赋值兼容性决定,即使在 implements 和 extends 子句的情况下也是如此。

在您的情况下Foo,等同S于规范,并且Bar是等同于规范的超类型(或超集)T

但是,我不确定这个答案是否让您满意,因为您也可以编写bar('x' as Bar);并且编译器不会显示错误,尽管您的类型中没有包含“x”。

我认为您最好的方法是简单地使用function bar(x: Bar). 然后还允许您致电bar('c');


推荐阅读