TypeScript泛型类型断言

4
这是我在使用 TypeScript 过程中的观察总结。
下面是一些代码:
type someTypeEnum = '1';
type someOtherTypeEnum = '2' | '3';
type combinedTypeEnum = someTypeEnum | someOtherTypeEnum;

以下是第一个案例:

function typeAssertion<T extends combinedTypeEnum>(args: T): args is someTypeEnum {
    // The error i get
    // A type predicate's type must be assignable to its parameter's type.
    //  Type '"1"' is not assignable to type 'T'.
    return undefined;
}

我不理解为什么这个东西会失败,因为我们已经将参数限制为combinedTypeEnum,以防万一我们执行...

typeAssertion('4')

我们已经得到一个错误,指出'4'不是一个有效的参数,那么为什么args is someTypeEnum被认为是无效的谓词。
以下是第二个案例:
function typeAssertion(args: combinedTypeEnum): args is someTypeEnum {
    return undefined;
}

这似乎很正常,但如果我们这样做:-

function someFunction<T extends combinedTypeEnum>(args: T): T {
    if (typeAssertion(args)) {
        // args here is  'T & "1"' 
        args
    }
    return args
};

为什么我们有 T & "1" 而不只是 "1",我们明确声明它是某个类型枚举。
我非常好奇为什么要做出这样的决定。如果能够看到以不同方式完成的情况下会发生什么问题,那将非常有帮助。

为什么这个类型守卫函数需要是通用的? - kaya3
2个回答

1

更新:

或者更简单地说:

function typeAssertion(args: combinedTypeEnum): args is someTypeEnum {
    return args === "1";
}

请参见这个游乐场


原文:

这个问题也困扰了我很长时间。实际上,解决方案(至少在2021年是这样的,不确定在提出问题时是否是这样)是:

function typeAssertion<T extends combinedTypeEnum>(args: T): args is T & someTypeEnum {
    return args === "1";
}

从我理解的这个答案来看,这背后的想法是当你调用typeAssertion("2")时,T获取值"2"(字面类型"2"),这意味着你最终得到函数:

function typeAssertion(args: "2"): args is someTypeEnum

很明显这是没有意义的。我不确定解决方法(使用T &)更有意义,但它可以起作用:

type someTypeEnum = '1';
type someOtherTypeEnum = '2' | '3';
type combinedTypeEnum = someTypeEnum | someOtherTypeEnum;

function typeAssertion<T extends combinedTypeEnum>(args: T): args is T & someTypeEnum {
    return args === "1";
}

const a: combinedTypeEnum = "1"
const b: combinedTypeEnum = "2"
const c: combinedTypeEnum = "3"
const d = "1"
const e = "2"
const f = "4"

let one: "1" = "1"

if (typeAssertion(a)) one = a
if (typeAssertion(b)) one = b
if (typeAssertion(c)) one = c
if (typeAssertion(d)) one = d
if (typeAssertion(e)) one = e
if (typeAssertion(f)) one = f // this one gives an error

在 Playground 中查看 TypeScript Playground


1

extends在使用字符串字面量时并不太有意义。为了更容易解释,让我使用其他类型。考虑这三个类:

class Animal {}

class Dog extends Animal {}

class Cat extends Animal {}

当我们使用泛型时,实际类型由调用者设置:
function foo<T extends Animal>(arg: T) {}

foo(new Dog()); //T is Dog, equivalent to foo(arg: Dog) {}
foo(new Cat()); //T is Cat, equivalent to foo(arg: Cat) {}

现在你可能已经看到我们要去哪里了。让我们使用类型谓词:

function foo<T extends Animal>(arg: T): arg is Cat {}

当我们调用 foo(new Dog()) 时,最后一个示例就等同于这个:
function foo(arg: Dog): arg is Cat {}

当然,它不起作用,也没有意义。
至于你的第二个例子:变量的类型并未改变。关键在于通过断言特定类型,编译器允许您使用该类型的任何功能。

嗯,我很抱歉回复晚了,但我很好奇在这种情况下我们是否可以以某种方式使用重载。谢谢。 - Amol Gupta

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接