如何声明一个函数断言两个参数相等



在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这样的呼叫,这很容易出错。

在本例中,nameOfnameOf3都进行了类型检查,但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游乐场的完整示例。

最新更新