typescript 有没有一种方法来表示对象内部的类型保护?

z9ju0rcb  于 2023-04-22  发布在  TypeScript
关注(0)|答案(2)|浏览(151)

我想使用类型保护和如下的附加信息。

type Something = string | number;

const isSomething = (anything: unknown): {
    result: anything is Something,
    additionalInfo: any
} => {
    ...
}

...

const afterTypeGuard = isSomething(thing);
if (afterTypeGuard.result === true) {
    return {
        afterTypeGuard.additionalInfo,
        length: thing.length,
    }
} else {
    return (thing + 32) * 3;
}

...

但我无法将返回类型设置为result: anything is Something而不是result: boolean
有没有办法做到这一点?

dbf7pr2w

dbf7pr2w1#

简短的答案是no, you can't enhance the return type of a custom type guard,尽管正如jcalz所指出的,有一个相关的feature request
previous answer中描述了几种方法-“单独提取additionalInfo”,或者“将additionalInfo分配为类型保护的副作用”-但可能更接近于您所描述的是将原始值作为结果的一部分,使用缩小的类型。在一个简单的公式中,这可能是:

type GuardResult<T> = {
    result: true;
    value: T;
    additionalInfo: any;
} | { result: false }

const isString = (anything: unknown): GuardResult<string> =>
    typeof anything === "string"
        ?    { result: true, value: anything, additionalInfo: "foo" }
        :    { result: false }

function processThing(thing: unknown) {
    const stringGuard = isString(thing);
    if (stringGuard.result) {
        // Note: although `stringGuard.value === thing`,
        // `stringGuard.value is string` but `thing is unknown`
        return {
            additionalInfo: stringGuard.additionalInfo,
            length: stringGuard.value.length,
        }
    }
}

Playground
通过一些额外的工作,GuardResult可以扩展为互补类型(即当result为false时):

type GuardResult<V, T> = {
    result: true;
    value: T;
    additionalInfo: any;
} | {
    result: false;
    value: Exclude<V, T>;
}

const isString = <V>(anything: V): GuardResult<V, string> => (
    (typeof anything === "string")
        ?   { result: true,  value: anything, additionalInfo: "foo" }
        :   { result: false, value: anything as Exclude<V, string> }
);

function processThing(thing: Something) {
    const stringGuard = isString(thing);
    if (stringGuard.result) {  // stringGuard.value is string
        return {
            additionalInfo: stringGuard.additionalInfo,
            length: stringGuard.value.length,
        }
    } else {  // stringGuard.value is number
        return (stringGuard.value + 32) * 3;
    }
}

Playground / Expanded playground
沿着类似的路线,但有一点类型滥用,你可以沿着函数式编程中的Either模式做一些事情-让你的guard-like函数返回一对大致表示“成功”和“失败”的对,例如,如果inputValue是guarded类型,则返回[inputValue, undefined],否则返回[undefined, inputValue],在这两种情况下适当地缩小类型:

function guard<V, T>(value: unknown): [T, undefined] | [undefined, Exclude<V, T>] { ... }

这可能会有点混乱(参见this example快速而肮脏的实现),因为undefined的值泄漏了,但返回值确实以一种令人愉快的方式破坏了结构:

const [stringValue, notStringValue, additionalInfo] = isString(thing);
    if (stringValue) {
        // thing === stringValue
        alert(stringValue + " is a string");
    } else {
        // thing === notStringValue
    }
trnvg8h3

trnvg8h32#

你有没有尝试在条件语句中使用类型别名?

type Something = string | number;

type SomethingGuard<T> = {
    result: T extends Something ? true : false,
    additionalInfo: any
};

const isSomething = (anything: unknown): SomethingGuard<typeof anything> => {
    // type guard logic here
    return {
        result: typeof anything === "string" || typeof anything === "number",
        additionalInfo: "additional information"
    };
}

const thing: unknown = "hello";
const afterTypeGuard = isSomething(thing);
if (afterTypeGuard.result === true) {
    return {
        additionalInfo: afterTypeGuard.additionalInfo,
        length: (thing as string).length,
    }
} else {
    return (thing as number + 32) * 3;
}

相关问题