我试图在编译时将某些字符串字段限制为仅某些值。问题是这些值应该是可扩展的。 这是一个简单的例子:
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
}
错误是
键入&#39; Foobarbaz&#39;不满足约束&#39; Foobar&#39;。类型 &#39;&#34; BAZ&#34;&#39;不能分配给&#39; Foobar&#39;。
所以问题是:我可以在打字稿中限制&#39;类型&#39;仅限某些字符串,但是可以使用其他字符串扩展吗? 或者这是一个XY问题,并且有一个明显更好的解决方案吗?
Typescript 2.3.4但我认为如果那里有魔力,我可以升级到2.4。
答案 0 :(得分:9)
我认为您正在使用&#34;可扩展的&#34;与关键字extends
的含义不同。通过说类型是&#34;可扩展&#34;您已经说过,您希望加宽类型以接受更多值。但是当extends
类型的某个类型时,它意味着您缩小类型以接受更少的值。
SomeInterface<T extends Foobar>
基本上只能是以下四种类型中的一种:
SomeInterface<'FOO'|'BAR'>
:amember
可以是'FOO'
或'BAR'
SomeInterface<'FOO'>
:amember
只能'FOO'
SomeInterface<'BAR'>
:amember
只能'BAR'
SomeInterface<never>
:amember
无法取任何值我有点怀疑你的确想要什么,但只有你肯定知道。
另一方面,如果您希望定义SomeInterface<T>
,T
总是 FOO
或BAR
,也可能是其他一些string
值,你想要TypeScript没有提供的东西,这将指定T
的下限。像SomeInterface<T
super
Foobar extends string>
这样的东西,它不是有效的TypeScript。
但您可能只关心 amember
的类型,而不是T
。如果您希望amember
成为FOO
或BAR
,还有其他string
值,则可以这样指定:
interface SomeInterface<T extends string = never> {
amember: Foobar | T;
[key: string]: string;
}
其中T
只是您想要允许的额外文字的联合。如果您不想允许任何额外费用,请使用never
,或者只省略类型参数(因为我已将never
作为默认值)。
让我们看看它的实际效果:
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'
我是否回答了你的问题?希望有所帮助。
答案 1 :(得分:2)
如何在打字稿中将'type'限制为仅限某些字符串,但是可以将其扩展为其他字符串吗?
不知道你的X问题是什么,但你总是可以引入另一个泛型参数,并通过声明它扩展该参数来限制类型。 Typescript 2.3 supports default types for generic parameters,因此默认使用Foobar
,您可以像以前一样使用SomeInterface
和一个参数,当您需要它来扩展您明确提供的其他内容时:
type Foobar = 'FOO' | 'BAR';
interface SomeInterface<T extends X, X extends string=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
abstract class SomeClass<T extends X, X extends string=Foobar> {
private anotherMember: SomeInterface<T, X>;
}
type Foobarbaz = Foobar | 'BAZ';
class FinalClass extends SomeClass<Foobarbaz, Foobarbaz> {
}
<强>更新强>
我想我现在明白了这个问题。一种解决方案是将像Foobar这样的联合类型编码为某些人工接口类型的keyof
,用于仅表示键(值类型未使用且无关紧要)。这样,通过扩展界面,您可以“自然地”扩展键集:
interface FoobarKeys { FOO: { }; BAR: { } };
type Foobar = keyof FoobarKeys;
interface SomeInterface<X extends FoobarKeys = FoobarKeys> {
amember: keyof X;
[key: string]: string; // this really has to stay
}
abstract class SomeClass<X extends FoobarKeys = FoobarKeys> {
protected anotherMember: SomeInterface<X> = {
amember: 'FOO'
};
protected amethod(): void {
this.bmethod(this.anotherMember); // no error
}
protected bmethod(aparam: SomeInterface<X>): void {
}
}
// let's extend it
interface FoobarbazKeys extends FoobarKeys { BAZ: {} };
type Foobarbaz = keyof FoobarbazKeys;
class FinalClass extends SomeClass<FoobarbazKeys> {
private f() {
this.bmethod({amember: 'BAZ'})
}
}
答案 2 :(得分:0)
要实现所需的功能,可以通过&
符号使用intersection。
type Foobar = 'FOO' | 'BAR';
type FoobarBaz = Foobar | & 'BAZ'; // or: 'BAZ' | & Foobar