【问题标题】:TS2339: Property does not exist on union type - property string | undefinedTS2339:联合类型上不存在属性 - 属性字符串 |不明确的
【发布时间】:2018-11-23 05:45:01
【问题描述】:

我的联合类型有问题,如下所示:

type RepeatForm = {
    step:
        | {
              repeat: false;
          }
        | {
              repeat: true;
              from: undefined;
          }
        | {
              repeat: true;
              from: string;
              by?: string;
          };
};

我有以下函数,我想在其中获取 by 的值(如果存在):

export const getByField = (form: RepeatForm) => {
    if (form.step.repeat === false || form.step.from === undefined) {
        return null;
    }
    const x = form.step.from;
    return form.step.by;
};

我收到此错误:Property 'by' does not exist on type '{ repeat: true; from: undefined; } | { repeat: true; from: string; by?: string | undefined; }'. Property 'by' does not exist on type '{ repeat: true; from: undefined; }'.

这让我非常困惑,因为 TypeScript 知道 form.step.fromundefined 不同,他甚至将变量类型 x 插入到 string

这个问题的原因是什么?那么如何访问by 属性呢?

【问题讨论】:

    标签: typescript types typescript2.0 union-types


    【解决方案1】:

    原始PR 用于区分联合非常具体的事实是区分字段必须是string 文字类型(可以选择添加对booleannumber 文字类型的支持,这似乎有发生)。因此,您根据字段类型(stringundefined)进行区分的用例似乎不受支持。这不起作用,例如:

    let u!: { v: number, n: number } | { v: string, s: string}
    if(typeof u.v === 'number') {
        u.n // not accesible, type not narrowed 
    }
    

    我们可以使用条件类型和自定义类型保护来使事情正常进行:

    function isUndefined<T, K extends keyof T>(value : T, field: K) : value is Extract<T, { [P in K] : undefined }> {
        return !!value[field]
    }
    
    export const getByField = (form: RepeatForm) => {
        if (form.step.repeat === false || isUndefined(form.step, 'from')) {
            return null;
        }
        const x = form.step.from;
        return form.step.by;
    };
    

    我们还可以创建这个函数的通用版本,允许按任何类型进行缩小:

    type ExtractKeysOfType<T, TValue> = { [P in keyof T]: T[P] extends TValue ? P : never}[keyof T]
    
    function fieldOfType<T, K extends ExtractKeysOfType<T, string>>(value : T, field: K, type: 'string'): value is Extract<T, { [P in K] : string }>
    function fieldOfType<T, K extends ExtractKeysOfType<T, number>>(value : T, field: K, type: 'number'): value is Extract<T, { [P in K] : number }>
    function fieldOfType<T, K extends ExtractKeysOfType<T, boolean>>(value : T, field: K, type: 'boolean'): value is Extract<T, { [P in K] : boolean }>
    function fieldOfType<T, K extends ExtractKeysOfType<T, Function>>(value : T, field: K, type: 'function'): value is Extract<T, { [P in K] : Function }>
    function fieldOfType<T, K extends ExtractKeysOfType<T, symbol>>(value : T, field: K, type: 'symbol'): value is Extract<T, { [P in K] : symbol }>
    function fieldOfType<T, K extends ExtractKeysOfType<T, object>>(value : T, field: K, type: 'object'): value is Extract<T, { [P in K] : object }>
    function fieldOfType<T, K extends ExtractKeysOfType<T, undefined>>(value : T, field: K, type: 'undefined'): value is Extract<T, { [P in K] : undefined }>
    function fieldOfType<T, K extends keyof T, TValue extends T[K]>(value : T, field: K, type: new (...args:any[])=> TValue): value is Extract<T, { [P in K] : TValue }>
    function fieldOfType<T, K extends keyof T>(value : T, field: K, type: string| Function) :boolean {
        if(typeof type === 'string') {
            return typeof value[field] === type;
        } else {
            return value[field] instanceof type
        }
    }
    
    const getByField = (form: RepeatForm) => {
        if (form.step.repeat === false || fieldOfType(form.step, 'from', 'undefined')) {
            return null;
        }
        const x = form.step.from;
        return form.step.by;
    };
    
    
    let u: { v: number, n: number } | { v: string, s: string}={ v: 0, n : 10};
    if(fieldOfType(u, 'v', 'number')) {
        console.log(u.n);
    }
    
    class A {private a: undefined;}
    class B {private b: undefined;}
    let uc: { v: A, n: number } | { v: B, s: string} = Math.random() > 0.5 ? { v: new B(), s: '10' } : { v: new A(), n: 10 };
    if(fieldOfType(uc, 'v', A)) {
        console.log(uc.n)
    }
    

    【讨论】:

    • 你知道 TypeScript 是否计划支持它吗?
    • @KonradKlimczak 抱歉,我不知道有什么计划支持这一点,但可能存在问题,这似乎是一个很常见的场景。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2016-03-29
    • 2016-03-14
    • 2017-02-25
    • 2017-12-31
    • 2018-08-27
    • 2021-11-24
    • 2019-01-11
    相关资源
    最近更新 更多