Typescript가?를 지원합니까? 운영자? (그리고 그것이 무엇입니까?)


336

Typescript가 현재 안전한 탐색 연산자를 지원합니까 (또는 계획이 있습니까)?.

즉 :

var thing = foo?.bar
// same as:
var thing = (foo) ? foo.bar : null;

또한이 연산자의 더 일반적인 이름이 있습니까? (구글 적으로 구글하기가 어렵습니다).


3
@ mattytommo 당신은 그것을 C #에서 가지고 있는데, 그것은 null 병합 연산자라고하며 ?? 구문 weblogs.asp.net/scottgu/archive/2007/09/20/…
— basarat

2
@BasaratAli은 불행히도, 유착은 괜찮지 property ?? property2만, 당신이 시도하는 경우 property.company ?? property1.company와 propertynull입니다, 당신은 얻을 것NullReferenceException
— mattytommo

1
@mattytommo 감사합니다. 이제 '?.' 실제로 체인의 모든 null 참조를 흡수합니다. 단.
— basarat

9
@ mattytommo 이것은 현재 C #에 존재합니다. msdn.microsoft.com/en-us/library/dn986595.aspx
— Highmastdon

9
우리를 방문한 Microsoft 담당자는 Elvis 운영자라고 불렀습니다. 물음표는 Elvis의 머리카락과 그가 부르는 마이크처럼 보입니다.
— Zymotik

답변:


167

업데이트 : TypeScript 3.7부터 지원되며 선택적 체인 이라고합니다 : https://www.typescriptlang.org/docs/handbook/release-notes/typescript-3-7.html#optional-chaining

TypeScript 언어 사양 에서 그것에 대한 참조를 찾을 수 없습니다 .

CoffeeScript에서이 연산자를 호출 할 때까지이를 존재 연산자 (특히, 존재 연산자 의 "접근 변형 자")라고합니다.

에서 연산자에 커피 스크립트의 설명서 :

실재 연산자의 접근 자 변형을 ?.사용하여 일련의 속성에서 null 참조를 흡수 할 수 있습니다. .기본 값이 null 이거나 정의되지 않은 경우 도트 접근 자 대신 사용하십시오 .

따라서 실재 연산자 의 접근 자 변형 이이 연산자를 참조하는 적절한 방법 인 것 같습니다. TypeScript는 현재 그것을 지원하지 않는 것으로 보입니다 ( 다른 사람들 이이 기능에 대한 욕구를 표현 했지만 ).


28
"실존 연산자의 접근 자 변형" 당연히. 너무 까다 롭고 잊을 수는 거의 없습니다. :). 매우 철저한 답변에 감사드립니다.
— Marty Pitt

1
@MartyPitt 물론! 나는 a) 이와 같은 연산자를 더 많이 채택하고 (C # please!) b) 더 나은 이름 (링크 된 블로그 게시물의 "안전한 탐색"연산자가 좋은 링을 가지고 있음)을보고 싶다는 데 동의합니다.
— Donut

2
Angular는 이것을 템플릿으로 구현합니다 : angular.io/guide/…
— Enzoaeneas

5
다른 언어로는 "Elvis"연산자
— k0enf0rNL

4
그것은 타이프 라이터 3.7.0 (대한 발표 것 github.com/microsoft/TypeScript/issues/... )
— c_froehlich

146

단일만큼 좋지는 않지만 작동합니다.

var thing = foo && foo.bar || null;

원하는만큼 &&을 (를) 사용할 수 있습니다.

var thing = foo && foo.bar && foo.bar.check && foo.bar.check.x || null;

33
&&는 진술이 참인 한 평가합니다. true이면 마지막 값을 반환합니다. False이면 False로 평가 된 첫 번째 값을 반환합니다. 0, null, false 등일 수 있습니다. || true로 평가되는 첫 번째 값을 반환합니다.
— A. KR

34
막대가 정의되어 있지만 부울 false 또는 0과 같이 false로 평가되면 제대로 작동하지 않습니다.
— mt_serg

96

이는 ECMAScript 선택적 체인 사양에 정의되어 있으므로 이에 대해 논의 할 때 선택적 체인을 참조해야 합니다. 아마도 구현 :

const result = a?.b?.c;

이것의 길고 짧은 것은 TypeScript 팀이 ECMAScript 사양이 강화되기를 기다리고 있기 때문에 향후 구현이 중단되지 않을 수 있다는 것입니다. 만약 그들이 지금 무언가를 구현한다면, ECMAScript가 그들의 사양을 재정의한다면 큰 변화가 필요할 것입니다.

선택적 체인 사양 참조

어떤 것이 표준 JavaScript가 될 수없는 경우 TypeScript 팀은 적합하다고 생각되는대로 구현할 수 있지만 향후 ECMAScript 추가를 위해 다른 많은 기능과 마찬가지로 조기 액세스를 제공하더라도 의미를 보존하려고합니다.

바로 가기

따라서 다음과 같은 유형 변환을 포함하여 모든 JavaScript 펑키 연산자를 사용할 수 있습니다.

var n: number = +myString; // convert to number
var b: bool = !!myString; // convert to bool

수동 솔루션

그러나 다시 질문으로 돌아갑니다. JavaScript (유형 TypeScript)에서 비슷한 작업을 수행하는 방법에 대한 모호한 예가 있지만 실제로 그 기능이 우아하다는 것은 아닙니다.

(foo||{}).bar;

그래서 경우가 foo있습니다 undefined결과 undefined및 경우 foo정의라는 이름의 속성이되는 bar값을 가지고, 결과는 그 값입니다.

나는 넣어 JSFiddle에 예 .

더 긴 예제에서는 상당히 스케치처럼 보입니다.

var postCode = ((person||{}).address||{}).postcode;

체인 기능

사양이 아직 공개되지 않은 상태에서 더 짧은 버전을 원한다면 필자는이 방법을 사용합니다. 체인을 만족시킬 수 없거나 null / undefined로 끝나는 경우 표현식을 평가하고 기본값을 반환합니다 ( !=여기서는 중요합니다. 여기서 약간의 긍정적 인 저글링을 원하므로 사용하고 싶지 않습니다!== ).

function chain<T>(exp: () => T, d: T) {
    try {
        let val = exp();
        if (val != null) {
            return val;
        }
    } catch { }
    return d;
}

let obj1: { a?: { b?: string }} = {
    a: {
        b: 'c'
    }
};

// 'c'
console.log(chain(() => obj1.a.b, 'Nothing'));

obj1 = {
    a: {}
};

// 'Nothing'
console.log(chain(() => obj1.a.b, 'Nothing'));

obj1 = {};

// 'Nothing'
console.log(chain(() => obj1.a.b, 'Nothing'));

obj1 = null;

// 'Nothing'
console.log(chain(() => obj1.a.b, 'Nothing'));

1
흥미로운하지만 내 경우에는 (this.loop || {}).nativeElement말 Property 'nativeElement' does not exist on type '{}'. any this.loop대해서 typeof angular.io/api/core/ElementRef
— kuncevic.dev

@Kuncevic-1) 대신에 호환되는 기본값을 제공 {}하거나 2) 형식 어설 션을 사용하여 컴파일러를 침묵 시켜야합니다 .
— Fenton

foo실제 유용한 객체 라고 가정하십시오 . (foo || {}).bar일반적으로 {}와 같은 유형이 아니기 때문에 일반적으로 유형 스크립트에서 컴파일 되지 않습니다 foo. 그것은 @VeganHunter의 솔루션이 피하려고하는 문제입니다.
— Simon_Weaver 5

1
@Simon_Weaver (foo || {bar}). bar는 컴파일러가 원활하게 실행되도록하고 자세한 정보가 허용된다고 생각합니다.
— 하프

바이 가장 가능성이되지 않을 것 변수로 정의 된 경우 @harps 실제로는 컴파일
— Simon_Weaver

82

업데이트 : 예, 이제 지원됩니다!

방금 TypeScript 3.7과 함께 출시되었습니다 : https://devblogs.microsoft.com/typescript/announcing-typescript-3-7/

옵션 체인 이라고합니다 : https://devblogs.microsoft.com/typescript/announcing-typescript-3-7/#optional-chaining

그것으로 다음과 같이 :

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

다음과 같습니다.

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

이전 답변

당신이 당신의 의견 / 욕망을 음성 수 GitHub의에이를위한 오픈 기능 요청이 있습니다 : https://github.com/Microsoft/TypeScript/issues/16


36

2019 년 11 월 13 일 수정!

2019 년 11 월 5 일 현재 TypeScript 3.7이 출시되었으며 이제 ?. 선택적 체인 연산자 🎉🎉🍾🍾🎉 !!!를 지원 합니다 !!!

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


역사적인 목적으로 만 :

편집 : fracz 의견 덕분에 답변을 업데이트했습니다.

TypeScript 2.0 출시 (C #의 Safe Navigator) !.와 동일하지 않음?.

자세한 내용은이 답변을 참조하십시오.

https://stackoverflow.com/a/38875179/1057052

이것은 컴파일러에게 그 값이 널 (null)이거나 정의되지 않았 음을 알려줍니다. 이 것 없는 값이 null이거나 정의되어 있는지 확인하세요.

TypeScript 널이 아닌 어설 션 연산자

// Compiled with --strictNullChecks
function validateEntity(e?: Entity) {
    // Throw exception if e is null or invalid entity
}

function processEntity(e?: Entity) {
    validateEntity(e);
    let s = e!.name;  // Assert that e is non-null and access name
}

4
값이 정의 되었다고 주장? 하기 때문에 동일하지 않습니다 . 자동 실패 / 거짓으로 평가됩니다. 어쨌든 알아 ?
— fracz 2011

1
이제 생각해 보았습니다 ...이 대답은 C # 연산자가 수행하는 "안전한 탐색"을 수행하지 않기 때문에 무의미합니다.
— Jose A

5
그래도 내 질문에 대답했습니다. 나는 알고 있었다?. c #에서 typescript로 시도했습니다. 그것은 효과가 없었지만 나는 그것을 보았다!. 존재했지만 그것이 무엇을했는지 몰랐습니다. 나는 그것이 같은지 궁금해하고 Google 검색을 수행 하고이 질문에 대한 길을 찾았습니다.
— Llewey

11

Elvis (?.) 옵션 체인 연산자는 TypeScript 3.7에서 지원됩니다.

이를 사용하여 널값을 확인할 수 있습니다 cats?.miows. cats가 널이거나 정의되지 않은 경우 널을 리턴합니다.

선택적 메소드 호출에 사용할 수도 cats.doMiow?.(5)있습니다. 존재하는 경우 doMiow를 호출합니다.

속성 액세스도 가능합니다 : cats?.['miows'].

참조 : https://devblogs.microsoft.com/typescript/announcing-typescript-3-7-beta/


저를 정정하십시오. 그러나 Elvis 운영자는 최소한 Kotlin에 ?:있습니다. 당신은 참조가 있습니까?
— 08:24에


1
- 그것은 일반 JS 곧 지원 될 것 developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/...
— Mottie

1
타이프 스크립트 3.7 버전의 발표는 언급 : devblogs.microsoft.com/typescript/announcing-typescript-3-7
— 기요 Balássy

10

?.TypeScript 버전 2.0 에서는 연산자 가 지원되지 않습니다 .

그래서 나는 다음 기능을 사용합니다 :

export function o<T>(someObject: T, defaultValue: T = {} as T) : T {
    if (typeof someObject === 'undefined' || someObject === null)
        return defaultValue;
    else
        return someObject;
}

사용법은 다음과 같습니다.

o(o(o(test).prop1).prop2

또한 기본값을 설정할 수 있습니다.

o(o(o(o(test).prop1).prop2, "none")

Visual Studio에서 IntelliSense와 잘 작동합니다.


1
이것이 바로 내가 찾던 것입니다! typescript 2.1.6에서 작동합니다.
— Rajab Shakirov

5
또는 당신은 그것을 부를 수 elvis<T>;-)
— Simon_Weaver

3
Simon_Weaver, 나는 "슬픈 광대"를 호출 : O (
— VeganHunter

5

드디어 왔어요!

다음은 몇 가지 예입니다.

// properties
foo?.bar
foo?.bar()
foo?.bar.baz()
foo?.bar?.baz()

// indexing
foo?.[0]
foo?.['bar']

// check if a function is defined before invoking
foo?.()
foo.bar?.()
foo?.bar?.()

그러나 가정과 정확히 동일하게 작동하지는 않습니다.

평가하는 대신

foo?.bar

이 작은 코드 스 니펫에 우리는 모두 쓰는 데 익숙합니다.

foo ? foo.bar : null

실제로 평가

(foo === null || foo === undefined) ?
    undefined :
    foo.bar

빈 문자열, 0 또는 false와 같은 모든 잘못된 값에 작동합니다.

왜 컴파일하지 않는지에 대한 설명이 없습니다. foo == null


3

Phonetradr에서 작업하는 동안이 util 메소드를 작성 하여 Typescript를 사용하여 딥 속성에 안전하게 입력 할 수 있습니다.

/**
 * Type-safe access of deep property of an object
 *
 * @param obj                   Object to get deep property
 * @param unsafeDataOperation   Function that returns the deep property
 * @param valueIfFail           Value to return in case if there is no such property
 */
export function getInSafe<O,T>(obj: O, unsafeDataOperation: (x: O) => T, valueIfFail?: any) : T {
    try {
        return unsafeDataOperation(obj)
    } catch (error) {
        return valueIfFail;
    }
}

//Example usage:
getInSafe(sellTicket, x => x.phoneDetails.imeiNumber, '');

//Example from above
getInSafe(foo, x => x.bar.check, null);


멋있는!! 경고가 있습니까? 내가 쓰기에 약 20 게터와 래퍼 클래스를 가지고, 그들 모두는 반환의 다음과 같은 유형이 - 그리고 모든 필드가 null 체크해야return this.entry.fields.featuredImage.fields.file.url;
— Drenai

유일한 경고는 성능에 영향을 줄 수 있지만 다양한 JIT 사용자가이를 처리하는 방법에 대해 이야기 할 자격이 없습니다.
— 레이 Suelzer

2

일반적으로이 방법을 권장하지는 않지만 (성능 문제를 조심하십시오) 스프레드 연산자를 사용하여 객체를 얕게 복제 한 다음 속성에 액세스 할 수 있습니다.

 const person = { personId: 123, firstName: 'Simon' };
 const firstName = { ...person }.firstName;

'firstName'의 유형이 '전파'되었기 때문에 작동합니다.

find(...)null을 반환 할 수 있는 표현식이 있고 단일 속성이 필요할 때 가장 자주 사용 합니다.

 // this would cause an error (this ID doesn't exist)
 const people = [person];
 const firstName2 = people.find(p => p.personId == 999).firstName;

 // this works - but copies every property over so raises performance concerns
 const firstName3 = { ...people.find(p => p.personId == 999) }.firstName;

typescript가 형식을 유추하는 방식과 관련된 일부 경우가있을 수 있으며 컴파일되지는 않지만 일반적으로 작동합니다.



0

이전에 대답했듯이, 현재도 여전히 고려되고 있지만 지금까지 몇 년 동안 물에서 죽었습니다 .

기존 답변을 바탕으로 생각할 수 있는 가장 간결한 수동 버전이 있습니다.

jsfiddle

function val<T>(valueSupplier: () => T): T {
  try { return valueSupplier(); } catch (err) { return undefined; }
}

let obj1: { a?: { b?: string }} = { a: { b: 'c' } };
console.log(val(() => obj1.a.b)); // 'c'

obj1 = { a: {} };
console.log(val(() => obj1.a.b)); // undefined
console.log(val(() => obj1.a.b) || 'Nothing'); // 'Nothing'

obj1 = {};
console.log(val(() => obj1.a.b) || 'Nothing'); // 'Nothing'

obj1 = null;
console.log(val(() => obj1.a.b) || 'Nothing'); // 'Nothing'

누락 된 속성 오류로 인해 자동으로 실패합니다. 기본값을 결정하는 표준 구문으로 돌아가며 완전히 생략 할 수 있습니다.


이것은 간단한 경우에 효과적이지만 함수 호출과 같은 더 복잡한 작업이 필요하고 결과의 속성에 액세스하면 다른 오류도 삼킨다. 나쁜 디자인.

위의 경우 여기에 게시 된 다른 답변의 최적화 된 버전이 더 나은 옵션입니다.

jsfiddle

function o<T>(obj?: T, def: T = {} as T): T {
    return obj || def;
}

let obj1: { a?: { b?: string }} = { a: { b: 'c' } };
console.log(o(o(o(obj1).a)).b); // 'c'

obj1 = { a: {} };
console.log(o(o(o(obj1).a)).b); // undefined
console.log(o(o(o(obj1).a)).b || 'Nothing'); // 'Nothing'

obj1 = {};
console.log(o(o(o(obj1).a)).b || 'Nothing'); // 'Nothing'

obj1 = null;
console.log(o(o(o(obj1).a)).b || 'Nothing'); // 'Nothing'

더 복잡한 예 :

o(foo(), []).map((n) => n.id)

다른 방법으로 가서 Lodash '와 같은 것을 사용할 수도 있습니다 _.get(). 간결하지만 컴파일러는 사용 된 속성의 유효성을 판단 할 수 없습니다.

console.log(_.get(obj1, 'a.b.c'));

0

아직 (2019 년 9 월 현재), "안전한 탐색 연산자" 가 이제 Stage 3 에 있으므로 TypeScript에서 구현되고 있습니다.

이 문제를보고 업데이트하십시오.

https://github.com/microsoft/TypeScript/issues/16

몇몇 엔진에는 초기 구현이 있습니다.

JSC : https://bugs.webkit.org/show_bug.cgi?id=200199

V8 : https://bugs.chromium.org/p/v8/issues/detail?id=9553

SM : https://bugzilla.mozilla.org/show_bug.cgi?id=1566143

( https://github.com/tc39/proposal-optional-chaining/issues/115#issue-475422578을 통해 )

지금 지원하기 위해 플러그인을 설치할 수 있습니다 :

npm install --save-dev ts-optchain

tsconfig.json에서 :

// tsconfig.json
{
    "compilerOptions": {
        "plugins": [
            { "transform": "ts-optchain/transform" },
        ]
    },
}

6 개월 정도 지나면이 답변이 만료 될 것으로 예상되지만 그 동안 누군가에게 도움이 되길 바랍니다.


-1

_.get(obj, 'address.street.name')유형이없는 JavaScript에 적합합니다. 그러나 TypeScript 에는 실제 Elvis 연산자 가 필요 합니다!

당사 사이트를 사용함과 동시에 당사의 쿠키 정책과 개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.