我有一个接受string
参数的方法。但是,我想稍微约束一下这个参数。因此,我用我想接受的字符串创建了一个并集类型:
type Foo = 'a' | 'b';
现在,我有一个简单的方法:
function bar(foo: Foo) {
// do something with that string
}
我想做的是允许调用者传递其他字符串(除了a
和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中表达这种约束吗?
@jscalz的评论让我重新考虑了我的答案。
从经典OOP的角度来看,bar<T extends Foo>(arg: T)
中对泛型参数的extends
约束意味着T
具有Foo
和更多的所有性质。因此,有了这个约束,您可以放心地在bar
中假设arg
的行为至少与Foo
类似(并且具有相同的属性结构(。
然而,在您的情况下,T
既不是class
也不是interface
,而是字符串文字的并集,这就是extends
变得毫无意义的地方:
假设bar<T extends Foo>(arg: T)
按照您的意愿工作(如OP中所述(,也就是说,T
是位于Foo
之上的字符串文字的超集。这意味着,在bar()
函数中,您根本无法控制arg
保持的值——它可以保持任何类型的值,您也可以使用任意。
考虑到以上情况,控制它进入bar()
的唯一机会是使用function bar(args: Bar)
,其中Bar = Foo | 'c'
也允许您在不进行强制转换的情况下调用bar('c');
。
原始答案:
将type Bar
定义为交集类型似乎至少可以驯服typescript 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手册中指出了以下内容(我用粗体表示(:
出于实用目的,类型兼容性由赋值兼容性决定,即使在实现和扩展子句的情况下也是如此
在您的情况下,Foo
等同于规范中的S
,Bar
是您的超类型(或超集(,等同于T
规范。
但是,我不确定这个答案是否能满足您的要求,因为您也可以编写bar('x' as Bar);
,尽管您的类型中没有包含"x",但编译器不会显示错误。
我认为你最好的办法就是简单地使用function bar(x: Bar)
。这也允许您调用bar('c');