TS2339: Property does not exist on union type - property string | undefined

前端 未结 1 719
终归单人心
终归单人心 2021-02-09 08:03

I have problem with my union type, which looks like this:

type RepeatForm = {
    step:
        | {
              repeat: false;
          }
        | {
                 


        
1条回答
  •  忘了有多久
    2021-02-09 08:55

    The original PR for discriminated unions is very specific about the fact that the discriminating field must be a string literal type (with the option to add support for boolean and number literal types which seems to have happened). So your use case where you are discriminating based on the type of the field (string vs undefined) does not appear to be supported. This does not work for example:

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

    We could use conditional types and a custom type guard to make things work:

    function isUndefined(value : T, field: K) : value is Extract {
        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;
    };
    

    We can also create a general version of this function that allows narrowing by any type:

    type ExtractKeysOfType = { [P in keyof T]: T[P] extends TValue ? P : never}[keyof T]
    
    function fieldOfType>(value : T, field: K, type: 'string'): value is Extract
    function fieldOfType>(value : T, field: K, type: 'number'): value is Extract
    function fieldOfType>(value : T, field: K, type: 'boolean'): value is Extract
    function fieldOfType>(value : T, field: K, type: 'function'): value is Extract
    function fieldOfType>(value : T, field: K, type: 'symbol'): value is Extract
    function fieldOfType>(value : T, field: K, type: 'object'): value is Extract
    function fieldOfType>(value : T, field: K, type: 'undefined'): value is Extract
    function fieldOfType(value : T, field: K, type: new (...args:any[])=> TValue): value is Extract
    function fieldOfType(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)
    }
    

    0 讨论(0)
提交回复
热议问题