Подтвердить что ты не робот

Typescript безопасный навигационный оператор (?.) или (!.) и пустые пути свойств

Angular2 шаблоны имеют безопасный оператор (?.), но в компоненте .ts(typescript 2.0). Безопасный навигационный оператор (!.) Не работает.

Пример:

Этот TypeScript

if (a!.b!.c) { }

компилируется на этот JavaScript

if (a.b.c) { }

Но когда я запустил его, я получаю следующую ошибку:

Невозможно прочитать свойство 'b' undefined

Есть ли альтернатива:

if (a && a.b && a.b.c) { }

?

4b9b3361

Ответ 1

Обновление:

Необязательное предложение по созданию цепочки/безопасной навигации находится на этапе 3 и, вероятно, вскоре появится в Typescript soon


В настоящее время в машинописи нет оператора безопасной навигации (все еще в обсуждении).

!. - это оператор ненулевого утверждения - он просто говорит контролеру типов, что вы уверены, что a не является null или undefined.

Подробнее здесь

Ответ 2

Обновление:

Планируется в рамках версии 3.7
https://github.com/microsoft/TypeScript/issues/33352


Вы можете попробовать написать такую функцию.

Основным преимуществом этого подхода является проверка типов и частичный смысл.

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;
}

И использование (поддерживает до 5 параметров и может быть расширено):

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

Пример на детской площадке.

Ответ 3

Другой альтернативой, которая использует внешнюю библиотеку, является _.has() из Lodash.

Например

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

равно

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

РЕДАКТИРОВАТЬ: Как отмечено в комментариях, вы теряете на вывод типа Typescript при использовании этого метода. Например, если предположить, что один объект правильно напечатан, можно получить ошибку компиляции с (a && ab && abz), если z не определено как поле объекта b. Но используя _.has(a, 'b.z'), вы не получите эту ошибку.

Ответ 4

Новая библиотека под названием ts-optchain обеспечивает эту функциональность, и в отличие от решения lodash, она также обеспечивает безопасность ваших типов, вот пример того, как она используется (взято из 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'))();

Ответ 5

Опираясь на ответ @Pvl, вы можете включить безопасность типов в возвращаемое значение, если вы используете переопределения:

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;
}

Пример на (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; } interface Test { a: A; } interface A { b: B; } interface B { c: string; } const test: Test = { a: { b: null, }, }; const a = dig(test, 'a'); const b = dig(test, 'a', 'b'); rel="nofollow noreferrer">детской площадке.