이펙티브 타입스크립트 아이템 11- 20

Dante
9 min readSep 5, 2021

--

독서 후 개인적으로 정리한 내용을 기록한 글입니다.

아이템 11 잉여 속성 체크의 한계 인지하기

타입이 선언된 변수에 객체 리터럴을 할당할때 타입스크립트는 정의된 타입 이외의 잉여 속성이 함께 할당되었는지 체크한다.

구조적 타이핑 관점에서 생각하면, const r 에 할당한 객체 리터럴이 interface Room 의 모든 속성을 포함하기 때문에 에러가 발생하지 않아야 한다. 임시 변수를 도입해보면, 같은 객체를 할당했지만 타입 에러가 발생하지 않는다.

객체 리터럴을 할당한 첫 예제에서는 잉여 속성 체크라는 과정이 수행되었다. 이는 변수에 타입을 명시적으로 선언했을 때 우리가 기대하는 할당 가능 검사 와는 별도의 과정이라는 것을 알아야 한다.

Options 인터페이스 타입의 변수에는 documentHTMLAnchorElement 모두 할당이 가능한데, Options가 정말 넓은 타입이라는 것을 알 수 있다.

잉여 속성 체크엄격한 객체 리터럴 체크라고도 하며 객체리터럴을 사용하지 않은 할당이나 타입 단언문을 사용할 때에도 적용되지 않는다.

잉여 속성 체크를 원치 않는다면 인덱스 시그치러를 사용할 수 있다.

아이템 12 함수 표현식에 타입 적용하기

자바스크립트에서는 함수를 문정과 표현식으로 나타낼 수 있다.

함수 매개변수부터 반환값까지 전체를 함수 타입으로 선언하여 다른 함수 표현식에서 재사용할 수 있기 때문에, 문장 보다는 표현식으로 사용하는 것이 낫다.

만약 같은 타입 시그니처를 반복적으로 작성한 코드가 있다면, 라이브러리에서 불러오는 함수일 경우 이미 존재하는 함수 타입이 있는지 찾아보거나, 함수타입을 따로 분리해내는 것이 좋다.

아이템 13 타입과 인터페이스의 차이점 알기

면접에서 단골 질문으로 등장할 것 같은 주제이다. type, interface는 명명된 타입(named type) 이라고 부르며 두 가지중 어느 것을 사용하더라도 타입을 나타내는데 문제가 없다.

cf) 본 책에서는 interface의 접두어로 대문자 I, type의 접두어로 대문자 T를 사용하는 것을 추천하지 않는데, 이런 코드 스타일은 C#에서 비롯된 관례라고 한다. 현재는 지양해야할 스타일이며 표준 라이브러리에서도 일관성 있게 도입되지 않았으므로 사용하지 말자.

인터페이스는 타입을 확장할 수 있으며, 타입은 인터페이스를 확장할 수 있다.

인터페이스는 타입을 확장할 수는 있지만 유니온은 할 수 없다.

인터페이스는 NamedVariable 과 같은 타입을 표현할 수 없다.

튜플 타입은 인터페이스보다 타입으로 표현하는 것이 용이하다.

인터페이스로도 튜플과 비슷하게 구현할 수 있으나 prototype.concat과 같은 메서드를 사용할 수 없다.

인터페이스는 타입과 다르게 보강(augment)가 가능하다. 이렇게 속성을 확장하는 것을 선언 병합(declaration merging)이라고 한다.

아이템 14 타입 연산과 제너릭 사용으로 반복 줄이기

같은 코드를 반복하지 말라는 DRY(don’t repeat yourself)원칙이 있다. 타입 중복 또한 코드 중복만큼 피해야 한다. 타입이 에서 공유된 패턴을 제거하는 일은 자바스크립트에서 중복된 코드를 제거하는 것보다 생소하게 느껴질 수 있기 때문에 타입 간에 매핑하는 방법을 익힌다면 타입스크립트에서도 DRY 원칙을 지킬 수 있다.

전체 애플리케이션의 상태를 표현하는 State와 부분만 나타내는 PositionState 있을 때 속성 타입을 다시 작성하거나 State를 상속하지 않고 State의 부분집합으로 PositionState를 표현할 수 있다.

매핑된 타입을 사용해보자. 매핑된 타입은 Pick 유틸리티 타입으로 대체할 수도 있다.

타입에 이름을 붙여서 반복을 피하고 extends를 사용해서 인터페이스 필드의 반복을 피하는 것이 좋다. 제너릭 타입은 타입을 위한 함수와 같다. 타입을 반복하는 대신 제너릭 타입을 사용하여 매핑해보자. 표준 라이브러리에 정의된 Pick, Partial, ReturnType과 같은 제너릭 타입에 익숙해져야 한다.

아이템 15 동적 데이터에 인덱스 시그니처 사용하기

인덱스 시그니처는 객체의 키, 밸류를 다음과 같이 표현한 것이다.

1. 이때 key는 string, number, symbol 중 하나가 되어야 한다. 2 인덱스 시그니처는 잘못된 키를 포함해 모든 키를 허용한다. 3.name 대신 Name 과 같이 키를 표현해도 유효한 타입으로 인정된다. 4.키마다 다른 타입을 가질 수 없다. name이 string이면서 index라는 키가 있다고 할 때 number타입으로 지정할 수 없다. 5.자동완성 기능이 동작하지 않는다.

csv 파일과 같은 형식을 인풋으로 받아 parsing 하는 함수가 있다고 할 때, 행과 열에 어떤 이름을 가진 데이터가 있는지 모르므로 이렇게 동적인 데이터가 들어올 때는 인덱스 시그니처를 사용한다.

매핑된 타입을 사용하며 특정 키에 대해 다른 타입을 사용하기 원한다면 다음과 같이 할 수 있다.

런타임 때까지의 객체 속성을 알 수 없을 경우에는 인덱스 시그니처를 사용한다.

아이템 16 number 인덱스 시그니처보다는 Array, 튜플, ArrayLike을 사용하기

type coersion은 암시적 타입 강제라고 번역되며 자바스크립트에서 가장 이상한 부분 중에 하나다.

자바스크립트에서 객체란 키/값 쌍의 모음이다. 키는 문자열만 가능하다. 배열에서는 숫자 인덱스로 참조해도 런타임에 문자열로 자동 변환된다. 만약 키가 number만 사용되기 원한다면 이에 맞는 배열 타입을 정의할 수 있다.

Object.keys와 같은 구문은 여전히 문자열로 반환된다. 위의 예제 코드와는 다르게 for in 문에서는 실용적인 허용이 가능하다.

자바스크립트 엔진에서 for-in 루프는 for-of 또는 for 루프에 비해 몇 배나 느리므로 잘 판단해서 사용해야 한다. 인덱스 시그니처로 number 형식이 가능하다고 하더라도 실제로 런타임에서는 항상 string 타입으로 변환되기에 이 부분이 혼란을 가져다 줄 수 있다. string 대신 number를 인덱스 타입으로 사용하면 특별한 의미가 있다는 오해를 불러일으킬 수 있다.

특정 길이를 가진 튜플을 사용하고 싶다면 ArrayLike 을 사용할 수 있다.

아이템 17 변경 관련된 오류 방지를 위해 readonly 사용하기

배열을 인자로 받아 연산하는 함수는 인자가 참조하는 배열의 원본을 바꿀 수 있기 때문에 사이드 이펙트를 원치 않는 함수에서는 readonly 접근 제어자를 사용할 수 있다.

아래 함수는 사이드 이펙트를 발생한다. 연산이 끝나면 arr 는 빈 배열이 되기 때문이다.

접근 제어자를 사용해 개선해보자.

readonly number[]number 타입과 아래의 내용에서 구분된다. 배열의 요소를 읽을 수는 있지만 쓸 수는 없다. length를 읽을 수는 있지만 바꿀 수는 없다. 배열을 변경하는 pop을 비롯한 다른 메서드를 호출 할 수 없다.

readonly number[]number 타입보다 기능이 적으므로 number[]readonly number[] 의 서브 타입이다.

readonly 를 선언하면 타입 스크립트가 매개변수가 함수내에서 변경이 일어나는지 체크하며, 호출하는 쪽에서는 함수가 매개변수를 변경하지 않는다는 보장을 받게된다.

constreadonly의 차이점을 알아야 한다. readonly 는 얕게 동작한다.

아이템 18 매핑된 타입을 사용하여 값을 동기화 하기

리엑트에서는 props가 변경될 때 해당 컴포넌트와 자식 컴포넌트가 리렌더링되는데, 이벤트 핸들러 같이 눈에 보이는 요소가 아니라면 해당 부분이 바뀌어도 다시 렌더링 될 필요가 없을 수 있다.

최적화를 위해 다시 리렌더링이 필요한지 판단하는 함수를 만들어 보자. 아래 함수는 어떤 props가 추가되더라도 해당 props에 대해서도 항상 판단하기 때문에 필요한 업데이트를 놓치는 에러를 허용하지 않는다. 이렇게 처리하는 것을 보수적 접근법이라고 하며 실패에 닫힌 접근법이라고 한다.

실패에 열린 접근법으로 작성을 해보자. 특정 props에 한해서만 리렌더링을 허용하여 너무 자주 업데이트 되는 것을 막는다. 하지만 정작 필요할 때 뷰가 바뀌지 않을 수 있으므로 최적화라고 보긴 어렵다.

타입체커가 위와 같은 역할들을 대신하도록 만들자.

[k in keyof ViewProps]는 타입 체커에게 ViewProps와 동일한 속성을 가져야 한다는 것을 알려준다. REQUIRES_UPDATE 에 boolean 값을 가진 객체를 사용했다. 나중에 추가 속성이 더해질때 REQUIRES_UPDATE에서 에러가 발생할 것이다.

아이템 19 추론 가능한 타입을 사용해 장황한 코드 방지하기

자바스크립트를 타입스크립트로 변환할때 많은 타입 구문을 추가해야겠다는 생각이 들지만 사실 모든 변수에 타입을 선언하는 것은 비 생산적이다. 타입 추론이 된다면 명시적 타입 구문은 필요하지 않기 때문이다.

더 복잡한 객체도 추론이 가능하다.

배열의 경우도 동일하다. 타입스크립트는 입력받아 연산을 하는 함수가 어떤 타입을 반환하는지 추론할 수 있다.

비구조화 할당문도 동일하게 지역변수의 타입이 추론된다.

정보가 부족해서 타입스크립트가 타입을 판단하기 어려운 상황이 발생하기도 한다. 함수의 매개변수 타입이 그러하다. 라이브러리를 사용할 때 타입 정보가 기본적으로 제공된다면 콜백함수 매개변수 타입은 자동으로 추론된다. 아래 express를 사용하는 예제에서 request, response에 명시적 타입을 선언하지 않는다.

타입을 명시해야하는 상황이 있다. 잉여 속성 체크가 필요할 때이다.

추론이 가능하지만 함수의 반환에 타입을 명시한다면, 구현상의 오류가 함수를 호출한 곳까지 영향을 미치는 것을 막을 수 있다.

아이템 20 다른 타입에는 다른 변수 사용하기

자바스크립트에서는 한 변수에 다른 타입의 값을 선언해서 재사용해도 된다. 타입스크립트에서는 오류가 발생한다. id 12–12–12를 선언할 때 이미 string으로 추론이 되었기 때문이다. 변수의 값은 바뀔 수 있으나 타입이 바뀌지는 않는다.

타입을 바꿀 수 있게 하기 위해서는 타입을 더 작게 제한하는 것이다.

유니온 타입으로 타입체커 오류를 해결한다 하더라도 id를 사용할 때마다 어떤 타입인지 확인해야 하기 때문에 별도의 변수를 도입하는 것이 더 낫다. 또한 const 키워드를 사용할 수 있기 때문에 꼭 재할당이 필요한지 잘 판단하자.

--

--

No responses yet