在typescript中,您可以将一个值传递给一个函数,并让该函数断言该值为true,以便缩小类型。例如
function assertTrue(v: unknown, message: string): asserts v {
if (!v) {
throw new MySpecialError(message)
}
}
但是,如果您想要一个更高级别的函数来构造v
,该怎么办。例如
function assertEqual(v: unknown, expected: unknown) {
assertTrue(v === expected, `expected ${v} to be ${expected}`)
}
如果调用assertEqual
,则不会得到任何类型缩小。一个破解解决方案是
function assertEqual<T>(v: unknown, expected: T): asserts v is T {
assertTrue(v === expected, `expected ${v} to be ${expected}`)
}
但这需要像CCD_ 3这样的呼叫,这很容易出错。
在本例中,nameOf
和nameOf3
都进行了类型检查,但nameOf2
没有:
export type Staged =
| {
stage: 'pre-named';
}
| {
stage: 'named';
name: string;
};
class MySpecialError extends Error {}
function assertTrue(v: unknown, message: string): asserts v {
if (!v) {
throw new MySpecialError(message)
}
}
function nameOf(staged: Staged) {
assertTrue(staged.stage === 'named', 'must be named')
return staged.name
}
function assertEqual<T>(v: unknown, expected: T): asserts v is T {
assertTrue(v === expected, `expected ${v} to be ${expected}`)
}
function nameOf2(staged: Staged) {
assertEqual(staged.stage, 'named')
return staged.name
}
function nameOf3(staged: Staged) {
assertEqual<'named'>(staged.stage, 'named')
return staged.name
}
您可以从第二个参数中派生断言的泛型类型信息,如下所示:
TS游乐场
function assertStrictEquals <T>(actual: unknown, expected: T): asserts actual is T {
if (actual !== expected) throw new Error('Values not equal');
}
const expected = { msg: 'hello world' };
const actual: unknown = expected; // unknown
assertStrictEquals(actual, expected);
actual; // { msg: string; }
而且,正如future所指出的,您可以使用const
断言来指示编译器从参数推断字符串文字类型:
function nameOf2 (staged: Staged): string {
assertStrictEquals(staged.stage, 'named' as const);
return staged.name;
}
您可以创建一个断言函数,专门用于推断字符串文字值:
function assertIsString <T extends string>(actual: string, expected: T): asserts actual is T {
assert(actual === expected, 'Strings not equal');
}
尝试使用模板文字类型:
type AssertType<T> = `${any & T}`;
function assertEqual<K, T extends (unknown extends T ? AssertType<K> : K)>(v: unknown, expected: T): asserts v is T {
assertTrue(v === expected, `expected ${v} to be ${expected}`)
}
function nameOf(staged: Staged) {
assertTrue(staged.stage === 'named', 'must be named')
return staged.name
}
function nameOf2(staged: Staged) {
assertEqual(staged.stage, 'named')
return staged.name
}
function nameOf3(staged: Staged) {
assertEqual<string, 'named'>(staged.stage, 'named')
return staged.name
}
TS游乐场的完整示例。