Typescript 3.7.0 버전부터는 많은 개발자들이 고대하던 몇 가지 스펙이 추가되었다. 그래서 간단하게나마 소개해 보려고 한다.
cf) vs-code 사용자라면 "Typescript 버전 3.7 + 워크 스페이스 버전 3.7" 설정을 잊지 말자!
Optional Chaining
- typescript 3.7.0
- vs-code 1.41
ECMAScript 기능 중 많은 요구가 있었던 Optional Chaining이 TypeScript 3.7에서 추가되었다.
이미 많은 다른 언어에서는 지원되고 있었다. 물론 이름은 다를 수 있다.
- C#: Null-conditional operator
- Swift: Optional Chanining
- CoffeeScript: Existential operator
What is optional chaining?
null 또는 undefined가 있다면, 표현식 실행을 즉시(Immediately) 중지시킬 수 있다. 기존 조건식이 필요 없고 우리는 단순히 ?.라는 새로운 연산자만 추가하면 된다. 아래는 타입 스크립트를 사용한다면 많이 사용하는 표현식 중 하나이다.
if(data && data.item && data.item.id){
// ...
}
불필요한 nullish checks를 위해 && 오퍼레이터를 사용해 왔다.
물론 저 코드를 쓰는 것은 어렵진 않지만, 반복되는 코드로 인해 가독성이 떨어진다. 결국 원하는 건 id가 있기를 바라는 거 하나인데.
optaional chaining을 적용하면 조금 더 간단하고 멋진 코드가 된다.
if(data?.item?.id){
// ...
}
Syntax
설명은 아래 예시와 함께 하겠다.
obj?.prop // optional static property access
obj?.[expr] // optional dynamic property access
func?.(...args) // optional function or method call
Base case
a?.b // undefined if `a` is null/undefined, `a.b` otherwise.
a == null ? undefined : a.b
a?.[x] // undefined if `a` is null/undefined, `a[x]` otherwise.
a == null ? undefined : a[x]
a?.b() // undefined if `a` is null/undefined
a == null ? undefined : a.b() // throws a TypeError if `a.b` is not a function
// otherwise, evaluates to `a.b()`
a?.() // undefined if `a` is null/undefined
a == null ? undefined : a() // throws a TypeError if `a` is neither null/undefined, nor a function
// invokes the function `a` otherwise
기능
1. Optional Element에 접근할 수 있다.
function getArray<T>(arr?: T[]){
return arr?.[0];
}
위 예제를 기존의 방식으로 바꿔보면 아래와 동일한 코드이다.
function getArray<T>(arr?: T[]){
return (arr === null || arr === undefined) ? undefined : arr[0];
}
2. Optional call이 가능하다.
async function makeRequest(url: string, log?: (msg: string) => void) {
log?.(`Request started at ${new Date().toISOString()}`);
// roughly equivalent to
// if (log != null) {
// log(`Request started at ${new Date().toISOString()}`);
// }
const result = (await fetch(url)).json();
log?.(`Request finished at at ${new Date().toISOString()}`);
return result;
}
더 나아가기
Long short-circuiting
단락이 트리거 될 때 .? 뒤에 오늘 모든 함수 호출, 프로퍼티 접근 등을 모두 건너뛴다.
a?.b.c(++x).d // if `a` is null/undefined, evaluates to undefined. Variable `x` is not incremented.
// otherwise, evaluates to `a.b.c(++x).d`.
a == null ? undefined : a.b.c(++x).d
Stacking
Optional Chaining뒤에 또 다른 Optional Chaining이 연속해서 올 수 있다.
a?.b[3].c?.(x).d
a == null ? undefined : a.b[3].c == null ? undefined : a.b[3].c(x).d
// (as always, except that `a` and `a.b[3].c` are evaluated only once)
왜 null이 아닌 undefined로 평가할까?
null은 프로퍼티가 없는 것으로 간주한다. null?.b는 undefined이다.
마이그레이션 후 단점이 있다면?
undefined를 할당해서 옵셔널 체이닝을 사용할 수 없는 어처구니 없는 상황이 생기기 때문에 결국 둘 다 쓰게 된다. 하나로만 통일하고 싶었지만, 현실적으로는 어렵다.
Nullish Coalescing
- Typescript - 3.7
- @babel/plugin-proposal-nullish-coalescing-operator
현재 ECMAScript stage 3에 있는 문법으로, 특별한 일이 없는 한 다음 명세에 포함될 것이다. 이 문법을 타입 스크립트 3.7 이상의 버전에서는 먼저 사용해 볼 수 있다.
현재(Present)
우리는 아래와 같은 코드 작성에 익숙하고 결과에도 익숙하다.
const response = {
settings: {
nullValue: null,
height: 400,
animationDuration: 0,
headerText: '',
showSplashScreen: false
}
};
const undefinedValue = response.settings.undefinedValue || 'some other default'; // result: 'some other default'
const nullValue = response.settings.nullValue || 'some other default'; // result: 'some other default'
근데 이 결과는 종종 예상치 못한 결과를 이끌어 낸다. 아래 예제의 값을 예상해 보자.
const headerText = response.settings.headerText || 'Hello, world!';
const animationDuration = response.settings.animationDuration || 300;
const showSplashScreen = response.settings.showSplashScreen || true;
정답
Potentially unintended. '' is falsy, result: 'Hello, world!'
Potentially unintended. 0 is falsy, result: 300
Potentially unintended. false is falsy, result: true
예상한 값이 맞았더라도, 과연 저 코드를 저런 값이 나오기 위해 작성했을까? 자바스크립트는 ||에 대한 대한으로 새로운 ?? 이라는 아이를 가지고 왔다.
What is Nullish Coalescing?
굳이 해석해 보자면 “널한(?) 병합”이다. “Nullish”라는 단어가 없어서 정확하게는 잘 모르겠다.
오히려 기존에 존재하던 널 병합 연산자가 맞는 표현 같다(https://en.wikipedia.org/wiki/Null_coalescing_operator).
이 널 병합 연산자는 기존에 ||가 0, 비어있는 문자열 등까지 false로 평가했다면, 이번 ?? 이아이는 똑똑하게 null과 undefiend만 평가한다.
Syntax
일단 문법은 간단하다. 위에서 말한대로 ??만 || 대신 써주면 된다.
위에 예제를 그대로 바꿔보면 이제 신세계가 펼쳐진다.
const response = {
settings: {
nullValue: null,
height: 400,
animationDuration: 0,
headerText: '',
showSplashScreen: false
}
};
const undefinedValue = response.settings.undefinedValue ?? 'some other default'; // result: 'some other default'
const nullValue = response.settings.nullValue ?? 'some other default'; // result: 'some other default'
const headerText = response.settings.headerText ?? 'Hello, world!'; // result: ''
const animationDuration = response.settings.animationDuration ?? 300; // result: 0
const showSplashScreen = response.settings.showSplashScreen ?? true; // result: false
Transpiling
'Typescript' 카테고리의 다른 글
[Typescript] 타입 시스템 (0) | 2019.12.21 |
---|---|
What is Typescript? (0) | 2019.12.19 |
[Typescript] 데코레이터(Decorator) (0) | 2019.12.09 |
[TypeScript] 타입스크립트 조건부 타입(Conditional Type) (0) | 2019.12.08 |
[TypeScript] 타입스크립트 제네릭 (0) | 2019.12.08 |