Safe navigation operator (?.) or (!.) and null property paths

前端 未结 6 1140
清歌不尽
清歌不尽 2020-11-27 14:32

In Angular 2 templates safe operator ?. works, but not in component.ts using TypeScript 2.0. Also, safe navigation operator (!.) doesn

相关标签:
6条回答
  • 2020-11-27 15:12

    Building on @Pvl's answer, you can include type safety on your returned value as well if you use overrides:

    function dig<
      T,
      K1 extends keyof T
      >(obj: T, key1: K1): T[K1];
    
    function dig<
      T,
      K1 extends keyof T,
      K2 extends keyof T[K1]
      >(obj: T, key1: K1, key2: K2): T[K1][K2];
    
    function dig<
      T,
      K1 extends keyof T,
      K2 extends keyof T[K1],
      K3 extends keyof T[K1][K2]
      >(obj: T, key1: K1, key2: K2, key3: K3): T[K1][K2][K3];
    
    function dig<
      T,
      K1 extends keyof T,
      K2 extends keyof T[K1],
      K3 extends keyof T[K1][K2],
      K4 extends keyof T[K1][K2][K3]
      >(obj: T, key1: K1, key2: K2, key3: K3, key4: K4): T[K1][K2][K3][K4];
    
    function dig<
      T,
      K1 extends keyof T,
      K2 extends keyof T[K1],
      K3 extends keyof T[K1][K2],
      K4 extends keyof T[K1][K2][K3],
      K5 extends keyof T[K1][K2][K3][K4]
      >(obj: T, key1: K1, key2: K2, key3: K3, key4: K4, key5: K5): T[K1][K2][K3][K4][K5];
    
    function dig<
      T,
      K1 extends keyof T,
      K2 extends keyof T[K1],
      K3 extends keyof T[K1][K2],
      K4 extends keyof T[K1][K2][K3],
      K5 extends keyof T[K1][K2][K3][K4]
      >(obj: T, key1: K1, key2?: K2, key3?: K3, key4?: K4, key5?: K5):
      T[K1] |
      T[K1][K2] |
      T[K1][K2][K3] |
      T[K1][K2][K3][K4] |
      T[K1][K2][K3][K4][K5] {
        let value: any = obj && obj[key1];
    
        if (key2) {
          value = value && value[key2];
        }
    
        if (key3) {
          value = value && value[key3];
        }
    
        if (key4) {
          value = value && value[key4];
        }
    
        if (key5) {
          value = value && value[key5];
        }
    
        return value;
    }
    

    Example on playground.

    0 讨论(0)
  • 2020-11-27 15:26

    Update:

    Planned in the scope of 3.7 release
    https://github.com/microsoft/TypeScript/issues/33352


    You can try to write a custom function like that.

    The main advantage of the approach is a type-checking and partial intellisense.

    export function nullSafe<T, 
        K0 extends keyof T, 
        K1 extends keyof T[K0],
        K2 extends keyof T[K0][K1],
        K3 extends keyof T[K0][K1][K2],
        K4 extends keyof T[K0][K1][K2][K3],
        K5 extends keyof T[K0][K1][K2][K3][K4]>
        (obj: T, k0: K0, k1?: K1, k2?: K2, k3?: K3, k4?: K4, k5?: K5) {
        let result: any = obj;
    
        const keysCount = arguments.length - 1;
        for (var i = 1; i <= keysCount; i++) {
            if (result === null || result === undefined) return result;
            result = result[arguments[i]];
        }
    
        return result;
    }
    

    And usage (supports up to 5 parameters and can be extended):

    nullSafe(a, 'b', 'c');
    

    Example on playground.

    0 讨论(0)
  • 2020-11-27 15:27

    A new library called ts-optchain provides this functionality, and unlike lodash' solution, it also keeps your types safe, here is a sample of how it is used (taken from the readme):

    import { oc } from 'ts-optchain';
    
    interface I {
      a?: string;
      b?: {
        d?: string;
      };
      c?: Array<{
        u?: {
          v?: number;
        };
      }>;
      e?: {
        f?: string;
        g?: () => string;
      };
    }
    
    const x: I = {
      a: 'hello',
      b: {
        d: 'world',
      },
      c: [{ u: { v: -100 } }, { u: { v: 200 } }, {}, { u: { v: -300 } }],
    };
    
    // Here are a few examples of deep object traversal using (a) optional chaining vs
    // (b) logic expressions. Each of the following pairs are equivalent in
    // result. Note how the benefits of optional chaining accrue with
    // the depth and complexity of the traversal.
    
    oc(x).a(); // 'hello'
    x.a;
    
    oc(x).b.d(); // 'world'
    x.b && x.b.d;
    
    oc(x).c[0].u.v(); // -100
    x.c && x.c[0] && x.c[0].u && x.c[0].u.v;
    
    oc(x).c[100].u.v(); // undefined
    x.c && x.c[100] && x.c[100].u && x.c[100].u.v;
    
    oc(x).c[100].u.v(1234); // 1234
    (x.c && x.c[100] && x.c[100].u && x.c[100].u.v) || 1234;
    
    oc(x).e.f(); // undefined
    x.e && x.e.f;
    
    oc(x).e.f('optional default value'); // 'optional default value'
    (x.e && x.e.f) || 'optional default value';
    
    // NOTE: working with function value types can be risky. Additional run-time
    // checks to verify that object types are functions before invocation are advised!
    oc(x).e.g(() => 'Yo Yo')(); // 'Yo Yo'
    ((x.e && x.e.g) || (() => 'Yo Yo'))();
    
    0 讨论(0)
  • 2020-11-27 15:29

    Since TypeScript 3.7 was released you can use optional chaining now.

    Property example:

    let x = foo?.bar.baz();
    

    This is equvalent to:

    let x = (foo === null || foo === undefined) ?
        undefined :
        foo.bar.baz();
    

    Moreover you can call:

    Optional Call

    function(otherFn: (par: string) => void) {
       otherFn?.("some value");
    }
    

    otherFn will be called only if otherFn won't be equal to null or undefined

    Usage optional chaining in IF statement

    This:

    if (someObj && someObj.someProperty) {
        // ...
    }
    

    can be replaced now with this

    if (someObj?.someProperty) {
        // ...
    }
    

    Ref. https://www.typescriptlang.org/docs/handbook/release-notes/typescript-3-7.html

    0 讨论(0)
  • 2020-11-27 15:33

    Another alternative that uses an external library is _.has() from Lodash.

    E.g.

    _.has(a, 'b.c')
    

    is equal to

    (a && a.b && a.b.c)
    

    EDIT: As noted in the comments, you lose out on Typescript's type inference when using this method. E.g. Assuming that one's objects are properly typed, one would get a compilation error with (a && a.b && a.b.z) if z is not defined as a field of object b. But using _.has(a, 'b.z'), one would not get that error.

    0 讨论(0)
  • 2020-11-27 15:34

    ! is non-null assertion operator (post-fix expression) - it just saying to type checker that you're sure that a is not null or undefined.

    the operation a! produces a value of the type of a with null and undefined excluded


    Optional chaining finally made it to typescript (3.7)

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