在else分支上为接口字段键入guard

时间:2017-08-27 19:56:44

标签: typescript types

interface Test<T> {
    field: string | T;
}

function isString<T>(test: Test<T>): test is Test<string> {
    return typeof test.field === "string";
}

function f<T>(test: Test<T>) {
    if (isString(test)) {
        const a = test.field; // the type of a is string
    } else {
        const b = test.field; // the type of b is string | T
    }
}

在上面的代码中,在if分支上,a的类型为string,这是正确的。但是,在else分支上,b的类型为string | T

即使我添加T的支票,我也会得到相同的结果:

function isT<T>(test: Test<T>): test is Test<T> {
    return typeof test.field !== "string";
}

function f<T>(test: Test<T>) {
    if (isString(test)) {
        const a = test.field; // the type of a is string
    } else if (isT(test)) {
        const b = test.field; // the type of b is string | T
    }
}

我不能明确地将b投射到T,就像我不需要将a投射到string一样?

1 个答案:

答案 0 :(得分:1)

问题是user-defined type guard正在检查Test<T>的类型,但您想要的是field的类型。

您的分支看起来像这样:

function f<T>(test: Test<T>) {
    if (isString(test)) {
        // We have a `Test<string>`
        const a = test.field;
    } else {
        // We do not have a `Test<string>`
        const b = test.field;
    }
}

if分支中,我们有Test<string>,其field属性是string | string的联合(只是string)。类型如下:

interface Test<string> {
    field: string | string;
}

else分支中,我们有Test<SomeNonString>,其field属性是string | SomeNonString的联合。它的类型如下:

interface Test<SomeNonString> {
    field: string | SomeNonString;
}

else分支中,我们需要消除歧义,因为field仍然是联合类型。只要Test<T>接口将field定义为string | T的联合类型,我们就需要在T不是string时进行后续测试。

以下是一个例子:

function isFieldString<T>(field: string | T): field is string {
    return typeof field === "string";
}

function f<T>(test: Test<T>) {
    if (isString(test)) {
        const s = test.field; // const s: string
    } else if (isFieldString(test.field)) {
        const s = test.field; // const s: string
    } else { 
        const t = test.field; // const t: T
    }
}