Effective TypeScript

silverj-kim·2021년 1월 18일
2

Ch1.

item 5. any 타입의 사용을 제한하라

TypeScript's type system is gradual and optional. 이것의 핵심은 any 타입이다.

There's No Type Safety with any Types.
any 타입과 함께인 타입 안전함은 없다.

let age: number;
age = '12';
age = '12' as any;

age 를 number로 선언했지만 any 타입은 string을 할당하는 것을 허락한다.
타입 체커는 그것이 number라고 믿을 것이다. 혼란이 생길 것 이다.

any Lets You Break Contracts.
any는 contract를 깨게 한다.

function calculateAge(birthDate: Date) : number {
//...
}
let birthDate: any = "1990-01-19";
calculateAge(birthDate); //OK

function 을 생성할 때 타입을 명시해주는데 만약에 any type을 사용하면 그 타입을 지키지 않더라도 에러가 발생하지 않는다. 자바스크립트가 종종 암묵적으로 타입들 사이를 변환하려고 하기 때문에 특히 문제가 될 수 있다.

There are No Language Services for any Types.
any 타입을 위한 언어 서비스는 없다.

interface Person {
  first: string;
  last: string;
}

const formatName = (p: Person) => `${p.first} ${p.last}`;
const formatNameAny = (p: any) => `${p.first} ${p.last}`;

타입스크립트는 원래 자동완성 기능을 제공해주는데 any 타입과 함께라면 제공해주지 않는다. 타입스크립트의 모토는 확장된 자바스크립트.

any Types mask bugs when you refactor code
any 타입은 너가 리팩토링할 때 버그를 숨긴다

타입을 작성하는 것은 귀찮을 수 있고 너는 단지 any를 쓰면 된다.
하지만 좀 더 자세한 타입을 사용했다면 type checker에 잡힐 것이고 any를 사용한다면 type checker에 안걸리고 런타임 exception이 발생할 수 있다.

interface ComponentProps {
  onSelectItem: (item: any) => void;
}
function renderSelector(props: ComponentProps) { /* ... */ }

let selectedId: number = 0;
function handleSelectItem(item: any) { //하지만 여기가 any
  selectedId = item.id;
}

renderSelector({onSelectItem: handleSelectItem});

//아래와 같이 item의 타입을 number로 변경
interface ComponentProps {
  onSelectItem: (id: number) => void;
}

any hides your type design.
any 타입은 너의 타입 디자인을 숨긴다.

수십가지의 프로퍼티에 대한 타입을 쓰는 것 보다 너는 단지 any 타입을 쓰는 게 더 쉬워보일 수 있다. 좋은 디자인은 clean, correct and understandable 코드를 작성하는 것이 필수적이다. 하지만 any 타입은 암묵적이다. Better to write it out for everyone to see. 이부분이 any type을 썻다는 걸 적으라는 건지 아니면 타입을 적으라는 건지?

any type undermine confidence in the type system.
any 타입은 타입시스템의 신뢰를 훼손한다.

타입스크립트는 나의 실수를 많이 잡아주는데 any 타입을 쓰면 잡히지 않을 때가 있어서 타입스크립트의 신뢰를 떨어뜨린다.

정리!!
사용하지마라!!!

Chapter 2. TypeScript's Type System.

타입스크립트의 타입 시스템

타입스크립트는 코드를 생성하지만 타입 시스템이 메인 이벤트다. 이것이 너가 이 언어를 사용하는 이유다.단단한 기초를 배울 수 있는 챕터

Item 6. Use Your editor to interrogate and explore the type system.

타입시스템의 탐색하고 조사하기 위해 에디터를 사용해라

너가 타입스크립트를 설치할 때 너는 2가지 실행파일을 얻는다.
1. tsc, 타입스크립트 컴파일러
2. tsserver, 타입스크립트 독립형 서버

너는 타입스크립 컴파일러를 직접적으로 실행할 가능성이 높지만 서버는 언어 서비스를 제공하기 때문에 모든 부분이 매우 중요하다.?
자동완성, 조사, navigation, refactoring을 제공하도록 구성되어있지 않다면 너는 놓칠 것이다. 자동완성은 타입스크립트의 좋은 점 중 하나.

조건식의 분기에서 타입의 변화를 보는 것은 타입 시스템의 신뢰를 쌓을 수 있는 엄청난 방법이다.

function logMessage(messgae: string | null) {
  if(message) {
    message //(parameter) message: string)
  }
}

분기 외부에 null, 내부에 string

function getElement(elOrId: string|HTMLElement|null): HTMLElement {
  if (typeof elOrId === 'object') {
    return elOrId;
 // ~~~~~~~~~~~~~~ 'HTMLElement | null' is not assignable to 'HTMLElement'
  } else if (elOrId === null) {
    return document.body;
  } else {
    const el = document.getElementById(elOrId);
    return el;
 // ~~~~~~~~~~ 'HTMLElement | null' is not assignable to 'HTMLElement'
  }
}

javascript의 typeof null은 "object"다. 그래서 elOrId는 여전히 null일 수 있다 따라서 먼저 null을 체크하도록 수정해야 한다. 그리고 2번째 에러는 getElementById 는 null을 반환할 수 있기 때문에 발생한다. 따라서 추가 헨들링이 필요하다.

"Go to Definition" 그 타입을 선언한 파일로 이동한다.

타입 선언은 처음에 읽기 어려울 수 있지만 타입스크립트로 할 수 있는 것, 사용중인 라이브러리의 모델링 방법 및 오류를 디버깅하는 방법을 확인할 수 있는 훌륭향 방법이다.

정리!!

  • 에디터를 써서 타입스크립트를 이용해라
  • 타입 시스템이 어떻게 작동하고 타입스크립트가 타입을 어떻게 추론하는지에 대한 감을 얻기위해서 에디터를 사용해라
  • 타입 선언 파일로 점프하는 방법

item 7. Think of types as Sets of Values.어렵..

값의 세트로서 타입을 생각

런타임에서 모든 변수는 자바스크립트의 값 범위 중 하나를 선택한다. 많은 값들이 있다.
그러나 코드를 실행하기 전에 타입스크립트는 에러를 체크할 때 변수는 타입을 가진다.
가능한 값들의 세트로서 생각하는 것이 최고다. 이 세트는 그 타입의 도메인(범위)으로 알려져있다.
가장 작은 세트는 아무런 값도 포함하고 있지 않은 empty set. 그것은 타입스크립트의 never 타입에 해당한다. 그것의 도메인은 비어져 있기 때문에 아무런 값이 할당될 수 없다.
다음으로 작은 세트는 단일 값이 포함된 것이이다. 이것들은 타입스크립트의 유닛 타입으로 알려진 또는 리터럴타입 에 해당된다.

리터럴 : 데이터 그 자체, 값을 할당할 때 사용된다.
javascript's literal : string, number, boolean, null, undefined, Symbol, object

너는 유닛 타입들을 union 할 수 있다. (합칠 수 있다?)
Union types correspond to unions sets of values.

type checker가 하는 거의 모든 작업은 한 세트가 다른 세트의 하위 집합(subset)인지 테스트하는 것

유한한 집합은 추론하기 쉽다. 그러나 무한 도메인이 있고 그 추론은 더 어렵다.

타입스크립트의 구조 입력 룰은 값이 다른 속성을 가질 수도 있음을 의미한다. 심지어 호출할 수도 있다. 이 사실은 가끔씩 과도한 프로퍼티 검사에 의해 가려질 수 있다.

interface Person {
  name: string;
}
interface Lifespan {
  birth: Date;
  death?: Date;
}
type PersonSpan = Person & Lifespan;
const ps : PersonSpan = {
  name: "Alan Turing",
  birth: new Date("1923/12/11"),
  death: new Date("1955/12/11")
} //OK
type K = keyof (Person | Lifespan); //Type is never

& 연산자는 두 가지 타입의 교차점을 계산한다.
Person과 Lifespan에 공통 속성이 없어서 empty set(never type)이라고 생각할 수 있다. 하지만 type operations는 인터페이스의 속성이 아닌 값 집합에 적용된다.
3가지 이상의 속성을 가져도 여전히 PersonSpan 타입에 속한다. 일반적인 규칙은 intersection(교차점) 타입의 값에는 속성의 합(union) 을 포함한다.
그러나 두 인터페이스의 union의 타입은 never다.

keyof (A&B) = (keyof A) | (keyof B)
keyof (A|B) = (keyof A) & (keyof B)

keyof
타입 값에 존재하는 모든 프로퍼티의 키값을 union 형태로 리턴받는다
ex. type LifeKeys = keyof LifeSpan; //birth, death

interface Person {
  name: string;
}
interface PersonSpan extends Person {
  birth: Date;
  death?: Date;
}

값의 세트로 타입을 생각할 때 extends의 의미는 무엇인가?
단지 "할당가능" "하위 집합"
PersonSpan의 모든 값은 string 타입의 name 속성을 무조건 가져야 한다. 그리고 birth도.

interface Vector1D {x: number}
interface Vector2D extends Vector1D {y: number}
interface Vector3D extends Vector2D {z: number}

1차원, 2차원, 차원 벡터의 관점에서 subtype을 설명한다면
Vector3D는 Vertor1D의 subset인 Vertor2D의 subset(하위 집합).
보통 계층구조(하이어라키)로 그려지지만 값의 집합으로 생각하면 밴다이어그램이 낫다.

extends 키워드는 또한 generic 타입에서도 제약 조건으로 나타날 수 있다. subset of 를 의미하기도 한다.
집합의 관점에서 생각해보면, 어떤 종류의 도메인이라도 string의 subset이면 된다.

Union 타입은 계층구조로는 맞지 않을 수 있지만 sets of value의 관점에서는 생각할 수 있다.

Tuple
const nameAndHeight: [string, number] = ['aaa', 177];
튜플 타입을 이용해 원소의 수와 타입을 지정된 배열의 타입을 정의할 수 있다.

const list = [1,2]; //Type is number[]
const tuple: [number, number] = list; //error 

number[]은 [number,number]의 하위집합이 아니기 때문에 할당할 수 없다. (역할당은 가능하다). 예를들어 [], [1]은 number[]은 맞지만 [number, number]는 아니기 때문.
속성의 길이가 다르기 때문에 triple을 pair에 할당하는 것도 안된다. 타입스트립트는 {0: number, 1: number}를 {0: number, 1: number, length: 2}로 모델링하기 때문.

모든 sets of values(값의 세트)가 타입스크립트 타입에 해당하는 것은 아니다.
Exclude 를 이용하여 유형을 뺄수 있으나 적절한 Typescript type이 될 경우에만 제외된다. (??)

Exclude<T,U>
T타입들 중 U타입과 겹치는 타입을 제외한다.
type Exclude<T, U> = T extends U ? never : T;
예시: tyep T0 = Exclude<"a" | "b", "a"> //"b"

정리!!

  • sets of values(타입의 도메인)으로 타입을 생각하라.
  • 타입스크립트 유형은 엄격한 하이어라키 보다 교차집합(밴다이어그램)을 형성한다. 두 가지 유형은 다른 유형의 하위 유형없이 겹칠 수 있다.
  • 추가적인 속성을 가지더라도 오브젝트는 여전히 타입에 속할 수 있다.
  • 타입 연산은 세트의 도메인을 적용된다. A와 B의 교차점은 A의 도메인과 B의 도메인의 교차점이다. 오브텍트 타입의 경우, A&B는 A와 B 모두의 속성을 갖는 것을 의미한다.
  • "extends"는 "assignable to" , "subtype of"는 "subset of"와 동의어

Item8. Know how to tell wheteher a Symbol is in the type space or value space

타입 공간 또는 값 공간에 Symbol이 있는지 확인하는 방법에 대해 알아보자

타입스크립트의 Symbol은 2가지 space(공간)가 존재한다.

  • Type space
  • Value space

동일한 이름이 어떤 공간에 있느냐에 따라 다른 것을 참조할 수 있기때문에 혼란을 줄 수 있다.

interface Cylinder {
  radius: number;
  height: number;
}
const Cylinder = (radius: number, height: number) => ({radius, height});

interface Cylinder는 type space 안에 있는 심볼이다.
const Cylinder는 value space 안에 있는 동일한 이름의 심볼이다.
서로 아무런 관계가 없다.

instanceof 는 자바스크립트의 런타임 연산자이며 그것은 값으로 작동한다.
그래서 instanceof Cylinder는 타입이 아니라 function 이다.

type sapce인지 value space인지 한눈에 알 수 있는 것은 아니다. symbol이 발생하는 컨텍스트를 통해 알 수 있다.

일반적으로 type or interface 뒤에 있는 symbol은 type space,
const or let 선언에 도입된 symbol은 value

컴파일 하면서 Type은 지워지기 때문에 생성된 자바스크립트를 보면 type space인지 value space인지 명확하게 알 수 있다.

타입스크립트에서는 type space와 value space을 번갈아 사용할 수 있다.
: 또는 as 뒤에 오는 심볼은 type space, = 뒤에 있는 심볼은 value space

interface Person {
  first: string;
  last: string;
}
const p: Person = { first: "Jane", last: "Jacobs" };

Person은 타입, { first: "Jane", last: "Jacobs" } 은 값.

클래스에서 도입한 타입스크립트의 Type은 그것의 shape(속성과 메서드)을 기반으로 한다.

type T1 = typeof p;  // Type is Person
type T2 = typeof email;
    // Type is (p: Person, subject: string, body: string) => Response

const v1 = typeof p;  // Value is "object"
const v2 = typeof email;  // Value is "function"

typeof는 자바스크립트의 런타임 연산자고 그것은 string을 리턴한다.
그것은 Typescript type과 같지않다.
자바스크립트에는 6가지 런타임 타입이 있다 : string, number, boolean, undefined, object, function

class 키워드는 value와 type을 둘다 가지고 있기 때문에 컨텍스트에 달려있다.

const v = typeof Cylinder;  // Value is "function"
type T = typeof Cylinder;  // Type is typeof Cylinder
declare let fn: T;
const c = new fn();  // Type is Cylinder

declare let fn: T; ???????

InstanceType generic을 사용하여 instance type과 constructor type 사이를 이동할 수 있다.
type C = InstanceType<typeof Cylinder>;

obj[field]obj.field는 값 공간에서는 동일하지만 타입공간에서는 동일하지 않다. 다른 타입의 속성의 타입을 얻으려면 []를 사용해야 한다.

이해가 안가는 부분 ??

type PersonEl = Person['first' | 'last'];  // Type is string
type Tuple = [string, number, Date];
type TupleEl = Tuple[number];  // Type is string | number | Date

아 Tuple[number] index 에 number를 넣으면 그 값이 타입이 된다는 건가?
ex. Tuple[1] type is number

두 공간에서 서로 다른 의미를 갖는 다른 많은 구성요소가 있다.

  • javascript의 this 키워드는 value space의 this, type으로의 this는 this의 타입스크립트 타입, "다형성 polymorphic this" 하위클래스가 있는 메서드 체인을 구현하는데 유용

다형성

  • in value space, & and | 는 AND and OR, in type space, union 연산자 와 교차점이다.
  • const는 새로운 변수를 소개하지만 as const는 리터럴 또는 리터럴 표현식의 추론된 타입을 변경한다.
  • extends 는 subclass 또는 subtype 또는 제네릭의 제약을 를 정의할 수 있다.
  • in

destructuring 하려면

function email({person, subject, body}: {person: Person, subject: string, body: string}) {...}

정리!!

  • type space, value space 어디에 있는지 확인하는 방법에 대해 알아보자
  • 모든 값은 타입을 가지고 있다. 그러나 타입은 값을 가지고 있지 않다. type 과 interface는 타입 스페이스에만 존재한다
  • class 또는 enum은 유형과 값을 모두 도입한다.
  • "foo"는 string 리터럴 일 수도 있고 string 리터럴 타입일 수도 있다.
  • typeof 및 많은 키워드와 연산자는 타입 스페이스와 값 스페이스에서 다른 의미를 가진다.

Item 9. Prefer Type Declarations to Type Assertions.

타입 단언보다 타입 선언 선호하라.

타입스크립트에서 변수에 값을 할당하고 값을 지정하는 두가지 방법이 있다.

interface Person { name: string };

const alice: Person = { name: "Alice" };
const bob = { name: "Bob" }  as Person;
  1. 변수에 타입 선언을 추가하고 값이 타입을 준수하는지 확인한다.
  2. 타입 단언(주장, assertion). 타입스크립트에서 추론한 타입에도 불구하고 타입이 Person이라고 말한다.

타입 선언을 선호하는 이유는 값이 인터페이스를 준수하는지 varify하기 때문이다. 반면에 type assertion은 type checker에게 타입을 알려주기 때문에 에러를 침묵시킨다.

<Person>{} = {} as Person

이해가 잘??
But (name: Person) would specify the type of name as Person and allow the return type to be inferred, which would produce an error.

const people = ['alice', 'bob', 'jan'].map(
  (name): Person => ({name})
); // Type is Person[]

언제 type assertion을 사용해야하나? type checker를 사용할수 없는 context에서

prefix !는 boolean negation(부정)
suffix !는 값이 non-null이라는 assertion(주장)으로 해석된다.

타입 주장에는 제한이 있다 : 임의 타입간에 변환할 수 없다. 하위타입 간에 변환은 가능하나 하위 타입이 아닌 경우 불가능하다.

Every type is a subtype of unknown, so assertions involving unknown are always OK.
unknown 을 포함함녀 임의의 타입 사이에 변환이 가능해진다.

정리!!

  • 타입 선언을 더 선호하라
  • arrow function 의 반환 타입에 주석을 다는 방법
  • 타입스크립트에 없는 타입에 대해 알고 있을 때 non-null assertions과 type assertion을 사용해라

item 10. Avoid Object Wrapper Types (String, Number, Boolean, Symbol, BigInt)

자바스크립트는 object를 제외하고 string, number, booleans, null, undefined, symbol, bigint primitive value(원시 값)을 가지고 있다.

Primitives는 불변이고 메서드가 없기 때문에 object와 구별된다.
stirng primitive 은 메소드가 없지만 자바스크립트는 메소드가 있는 string object type 또한 정의한다. Javascript는 이런 유형 간 서로 자유롭게 변환이 된다. string primitive에서 charAt과 같은 메서드에 접근하는 경우 자바스크립트는 해당 메서드를 string 오브젝트로 감싸고 메서드를 호출한 다음 object를 버린다.

null과 undefined는 object wrapper가 없다

wrapper type은 원시값에 대한 메서드와 정적 메서드를 제공하지만 일반적으로는 인스턴스화 할 이유가 없다.

Typescript는 원시 요소와 오브젝트 래퍼에 대해 고유한 타입을 사용하여 이러한 차이를 모델링한다.

stringString에 할당할 수 있지만 Stringstring에 할당할 수 없다.

물론 런타임의 값은 object가 아니라 primitive다.
그러나 primitive type을 object wrapper에 할당할 수 있기 때문에 타입스크립트는 이러한 선언을 허용한다. primitive type을 사용하는 게 좋다.

BigIng와 Symbol values가 있다. type이 아니다. (??)

정리!!

  • 원시값에 대한 메서드를 제공하기 위해 오브젝트 래퍼 타입을 사용하는 법을 이해한다.
    인스턴스화 하거나 직접 사용하지 않도록 한다.
  • 타입스크립트 오브젝트 래퍼 타입을 피해라. 대신에 원시 타입을 사용해라.

Item 11. Recognize the Limits of excess property checking

속성 확인 초과의 제한을 알아보다.

선언된 타입의 변수에 객체 리터럴을 할당할 때 타입스크립트는 해당 타입의 프로퍼티를 가지고 있고 다른 속성은 없는지 확인한다.

interface Room {
  numDoors: number;
  ceilingHeightFt: number;
}
const r: Room = {
  numDoors: 1,
  ceilingHeightFt: 10,
  elephant: 'present',
// ~~~~~~~~~~~~~~~~~~ Object literal may only specify known properties,
//                    and 'elephant' does not exist in type 'Room'
};
const obj = {
  numDoors: 1,
  ceilingHeightFt: 10,
  elephant: 'present',
};
const r: Room = obj;  // OK

선언된 타입의 프로퍼티를 제외하고 다른 프로퍼티가 있을 경우 에러를 뱉지만
중간 변수를 사용하면 에러가 없어진다.

type assertion을 사용할 때 초과 프로퍼티 검사는 발생하지 않는다.
이것은 assertion보다 declaration을 더 선호하는 이유 중 하나다.

index signature를 사용하여 추가 프로퍼티를 기대하도록 지시할 수도 있다.

interface Options {
  darkMode?: boolean;
  [otherOptions: string]: unknown;
}
const o: Options = { darkmode: true };  // OK

이건 적합하지 않은 방법이다.

weak type 오직 옵션 필드만 가지고 있는 weak 타입
weak type의 경우 타입스크립트는 값 타입과 선언된 타입에 하나 이상의 공통 속성이 있는지 확인하기 위해 다른 검사를 추가한다.
모든 할당 가능성 검사 중에 발생하며 이 검사는 중간 변수를 사용해도 무시되지 않는다.

excess property checking은 오타를 잡고 실수를 잡는 데 효과적이다. 특히 옵션 필드가 포함된 타입의 경우. 그러나 범위가 매우 제한적이다. 객체 리터럴에서만 적용된다.

정리!!

  • object literal을 변수에 할당하거나 인수로 전달하면 excess property checking을 받는다.
  • 과도한 프로퍼티 검사는 에러를 찾는데 효과적인 방법이다. 그러나 일반적인 구조적 할당 가능성 검사와는 다르다. 서로 충돌하면 할당 가능성의 모델을 구축하기가 더 어렵다.
  • 과도한 프로퍼티 검사의 한계를 알고 있어야 한다. 중간 변수를 도입하면 에러가 없어진다.

item 12. Apply Types to Entire Function Expressions when possible

가능한 전체 함수 표현식에 타입을 적용하라

자바스크립트와 타입스크립트는 함수문과 함수식을 구별한다.

TS에서 함수 표현식의 장점은 매개변수의 타입과 반환 타입을 개별적으로 지정하는 대신 한번에 전체 함수에 타입 선언을 할 수 있다는 것이다.

적은 annotation, function 구현과 분리되어 있고 logic을 더 명백하게 만든다. 반환 타입도 체크한다.

라이브러리 작성자의 경우 , 일반적인 콜백에 대한 타입 선언을 제공하는 것을 고려해라.
ex. 리액트의 MouseEventHandler type

노이해 ??

const checkedFetch: typeof fetch = async (input, init) => {
  const response = await fetch(input, init);
  if (!response.ok) {
    throw new Error('Request failed: ' + response.status);
  }
  return response;
}

fetch 함수 를 타입으로 적용하면 타입스크립트는 fetch 함수 타입을 통해 input과 init parameter의 타입을 추론할 수 있다.
또 checkedFetch의 반환 타입이 fetch와 동일하다는 걸 보장할 수 있다.

간결 + 안정성이 향상, 타입 선언을 전체 함수에 적용할 수 있는지 여부를 고려하라.

정리!!

  • 매개변수와 리턴 타입이 아닌 전체 함수식에 타입 annotation을 적용하는 것을 고려하라
  • 같은 타입 시그니처가 반복된다면 function type을 고려하라
  • 다른 함수의 시그니처를 일치시킬 때 typeof fn을 사용하라

Item 13: Know the Differences Between type and interface

타입과 인터페이스의 차이점

타입스크립트에서 named type을 선언하길 원한다면 너는 두가지 옵션을 가진다.
type 또는 interface

type TState = {
  name: string;
  capital: string;
}
interface IState {
  name: string;
  capital: string;
}

클래스도 사용할 수 있지만 자바스크립트 런타임에서 값으로 생각한다

type과 interface 중 어떤 걸 써야하나?

접두사의 예는 이름이 I 또는 T인 타입 이름만 사용하여 정의되는 방법을 나타낸다. 그러나 요즘엔 불필요하고 나쁜 스타일로 여겨지고 있다.

먼저 State type은 거의 구별할 수 없다.

?? (x: number): number; 이거 머지?

type TFnWithProperties = {
  (x: number): number;
  prop: string;
}

class는 인터페이스와 간단한 타입으로 구현할 수 있다.

차이점
인터페이스는 union type처럼 복잡한 타입을 확장할 수 앖다. 그렇게 하려면 type과 & 을 써야한다.
union type은 있고 union interface는 없다.

일반적으로 타입이 인터페이스보다 좋다. union할 수 있으며 매핑된 기능이나 조건부 타입같은 확장된 기능을 이용할 수 있다.
튜플 및 어레이 타입을 보다 쉽게 표현.

그러나, 타입에는 없는 기능을 인터페이스는 몇가지 가지고 있다.
인터페이스는 augmented 증강될 수 있다. 같은 이름의 interface를 선언하여 필드를 추가할 수 있다. 이것을 "declaration merging"이라고 한다.
타입스크립트는 병합을 이용해 다른 버전의 자바스크립트 표준 라이브러리에 대한 타입을 가져온다. es5 + es2015

만약 아무도 너의 타입을 늘리지 않는 것이 중요할 땐 type을 써라.

복잡한 타입의 경우 타입을 사용해아한다. 하지만 단순한 오브젝트 타입은? 일관성과 확대를 고려하여 알아서 사용해라?
확립된 스타일이 없을 땐 augmentation 증가에 대해 생각해라.

정리!!

  • 타입과 인터페이스 차이와 유사성 이해

item 14.Use type operations and Generics to Avoid repeating yourself

반복을 피하기 위해 타입 연산 및 제네릭을 사용하라

DRY principle (원칙) : Don'y repeat yourslef (DRY) 반복하지마라.
타입 간 매핑 방법을 학습하여 DRY의 이점을 타입 정의에 적용할 수 있따.

반복을 줄이는 가장 간단한 방법은 타입을 지정하는 것.

interface Point2D {
  x: number;
  y: number;
}
function distance(a: Point2D, b: Point2D) {}

type HTTPFunction = (url: string, opts: Options) => Promise<Respons>;
const get: HTTPFunction = (url, opts) => {}
const post : HTTPFunction = (url, opts) => {}

또한 교차 연산차(&)를 이용하여 기존 타입을 확장할 수 있다. 하지만 다음과 같은 경우는 흔하지 않다.

type PersonWithBirthday = Pesron & {birth: Date};

TopNavState를 확장하여 State를 구축하는 대신 TopNavState를 State 필드의 하위 집합으로 정의하려고 한다. 이렇게 하면 전체 앱의 상태를 정의하는 단일 인터페이스를 유지할 수 있다. 중복되는 프로퍼티의 타입을 제거할 수 있다.

interface State {
  userId: string;
  pageTitle: string;
  recentFiles: string[];
  pageContents: string;
}
type TopNavState = {
  userId: State['userId'];
  pageTitle: State['pageTitle'];
  recentFiles: State['recentFiles'];
};

//better
type TopNavState = {
  [k in "userId" | "pageTitle" | "recentFiles" ]: State[k]  
};

type Pick<T, K> = { [k in K]: T[k] };

//최종
type TopNavState = Pick<State, 'userId' | 'pageTitle' | 'recentFiles'>;

반복문도 쓸 수 있네
이해 제대로 한거임?
Pick은 제네릭의 예. PICK은 T(State)와 K(userId | pageTitle | recentFiles) 두가지 타입을 사용하며 함수가 2개의 값을 사용하고 세가지를 반환할 수 있기 때문에 세가지 타입을 반환한다.

type ActionType = 'save' | 'load';
//better
type ActionType = Action['type'];

타입이 반복되어 각 속성을 ? 옵셔널로 설정하는 경우는 일반적이여서 표준 라이브러리에
Partial 로 포함되어있다.
typeof를 이용하여 값의 shape도 일치하는 타입을 정의할 수 있다
type Options = typeof INIT_OPTIONS

조건부 타입(conditional types) : ReternType 제네릭
type UserInfo = ReturnType<typeof getUserInfo>;
함수의 값인 getUserInfo가 아닌 함수 타입인 getUserInfo의 타입에 따라 작동한다. 신중하게 사용하라..

제네릭 타입은 타입에 대한 함수와 동일하다.그리고 함수들은 DRY의 열쇠이다.
따라서 제네릭이 타입의 DRY 핵심인건 놀라운 일이 아니다.
타입 시스템을 사용하여 매핑할 수 있는 값을 제한할 수 있다.

제네릭 타입의 파라미터는 어떻게 제한하냐? extends를 사용한다. 제네릭 매개변수가 타입을 확장한다고 선언할 수 있다.

note 현재 타입스크립트는 선언데 항상 generic 파라미터를 쓰길 요구한다. Typescript가 제네릭 매개변수의 타입을 추론하도록 하려면 신중하게 타입이 정의된 함수를 사용할 수 있다.

extends 를 subset of 로 읽는게 도움이 된다.

정리!!

  • DRY 원칙 적용
  • 타입을 반복하지 않고 이름을 정하라.인터페이스의 필드를 반복하지 마라. extends를 사용하라.
  • 타입 간에 매핑을 위해 Typescript에서 제공하는 tool 이해를 구축하라. keyof typeof indexing, and mapped types.
  • 제네릭 타입은 타입에 대한 함수와 동일하다. extends 를 사용하여 제네릭 타입을 제약하라
  • Partial, ReturnType, Pick 에 대해 숙지

item 15. Use index signatures for dynamic data

동적 데이터에는 index signature를 사용하라

Javascript 의 가장 좋은 기능 중 하나는 객체를 만드는 편리한 구문

인덱스 서명(index signature)을 이용하여 유연한 매핑을 할 수 있다.

type Rocket = {[property: string]: string};
// string is the index signature.
  • a name for keys : 단순히 문서화용, 타입 검사기에 어떤 방식으로도 사용되지 않음
  • a type for the key : string, number or symbol 을 조합해야 하지만 일반적으로 문자열만 사용
  • a type for the value: 무엇이든 될 수 있다.

index signature는 그다지 정확하지 않다. 이 경우 로켓은 인터페이스여야 한다

인덱스 서명을 사용하는 곳은 동적 데이터.
인덱스 서명을 사용하는 데 있어 문제가 있는 경우 string이 너무 광범위 하면 다른 방법들이 있다.

그 중 하나는 Record를 사용하는 것.
이것은 키 타입의 유연성을 높여주는 제네릭 타입니다. 특히 string의 하위 집합을 전달할 수 있다.

type Vec3D = Record<'x' | 'y' | 'z', number>;
// Type Vec3D = {
//   x: number;
//   y: number;
//   z: number;
// }

또 다른 하나는 매핑된 타입을 쓰는 것이다. 이렇게 하면 키마다 다른 타입을 사용할 수 있다.

type Vec3D = {[k in 'x' | 'y' | 'z']: number};
// Same as above
type ABC = {[k in 'a' | 'b' | 'c']: k extends 'b' ? string : number};
// Type ABC = {
//   a: number;
//   b: string;
//   c: number;
// }

item 16. Prefer arrays, tuples and arraylike to number index signatures

number index signature보다 array tuple arrylike를 더 선호하라

자바스크립트는 유별난 언어. 암묵적인 타입의 강압을 포함한다

"0" == 0 //true

위 와 같은 기능은 === 으로 방지할 수 있다.

object는 무엇인가? 키/값 쌍의 모음
키는 보통 문자열이고 값은 무엇이든 될 수 있음

해시
해시 함수? 임의의 길이의 데이터를 고정된 길이의 데이터로 매핑하는 함수.
해시 함수를 이용하여 고정된 길이의 암호화된 문자열로 바꿔버리는 것

자바스크립트는 "hashable" object에 대한 개념이 없다. 보다 복잡한 키를 사용하려는 경우 해당 오브젝트는 그것을 toString 을 호출하여 stirng으로 변환한다.
특히 숫자는 키로 사용할 수 없다. 런타임에서 문자열로 변환된다.

array는 무엇인가? 이것은 object다.
그러나 숫자 인덱스를 함께 사용하는 것은 매우 일반적.
object.keys를 사용해서 array의 키를 조회해보면 string이 반환된다.

item17. 변화와 관련된 오류를 방지하기 위해 readonly 사용

Chapter3. Type Inference

프로그래밍언어에서 “statically typed”와 ‘explicitly typed” 는 동의어로 사용되어 왔다.

item19. Avoid cluttering your code with inferable types.

추론 가능한 타입과 함께 너의 코드를 어지럽히지마라.

모든 변수에 타입 선언을 하는 것은 비생산적. The explicit type annotation is redundant.
타입스크립트는 너가 기대하는 것보다 더 정교한 타입 추론이 가능

추론된 타입을 쓰는 것은 리팩토링에 좀 더 좋을 수 있다.

destructuring 은 타입 추론이 된다.

차입스크립에서 변수의 타입은 보통 생성될 때 결정된다.
이상적인 타입스크립트 코드는 함수와 메소드에 대한 타입 선언을 포함하고 그 안에 있는 로컬변수에는 포함되어있지 않다.

파라미터에 타입 선언을 안하는 경우
1. default value
2. callback 함수로 사용될 때 (ex. express 라이브러리)

타입 선언을 원하는 경우 => 정확한 장소에 에러를 알 수 있다
1. 오브젝트 리터럴을 선언할 . 프로퍼티 타입 체크를 하는 게 안전
2. 함수의 반환 타입

린트 룰 no-inferrable-types 타입 선언이 필수

요약

  • 타입 추론이 가능할 땐 타입 선언을 피하라
  • 이상적인 타입스크립트 코드는 함수와 메소드에 대한 타입 선언을 포함하고 그 안에 있는 로컬변수에는 포함되어있지 않다
  • 오브젝트 리터럴과 함수 리턴 타입에는 선언을 사용해라

item20. use different variables for different types.

다른 타입에 다른 변수를 사용해라.

변수의 값은 변할 수 있지만 그것의 타입은 보통 변하지 않는다.

타입을 바꾸지않고 두 개의 변수를 선언하면 좋은 이유

  • 두가지의 연관되어있지않은 컨셉이 얽히지 않는다
  • 너가 좀 더 특정한 변수 이름을 사용할 수 있게 한다.
  • 타입 추론을 향상시킨다.(?)
  • 간단한 타입
  • const 로 변수를 선언하게 한다. 그건 사람에게나 타입 검사기에게나 좋다

다른 컨셉의 변수는 다른 이름을 사용하는 게 좋다.
릴트 룰로 shadowing하는 걸 허락하지않는다.

item21. understand type widening

타입 확장을 이해하라

런타임에서 모든 변수는 single value다.
타입스크립트가 너의 코드를 체크하는 정적 분석타임(?)에서는 변수는 가능한 value의 세트(=type)를 가진다.

타입스크립트는 똑똑하지만 내 마음을 읽을 수는 없다

변수의 타입은 선언 후에 변경될 수 없는 게 기본 룰

‘TypeScript is trying to strike a balance between specificity and flexibility.’
타입스크립트는 특수성과 유연성 사이에서 발란스를 맞추려고 애쓴다

타입스크립트의 확장을 컨트롤하는 몇가지 방법

  • const는 만병통치약
    그러나 오브젝트와 어레이에서는 노 해결책 : object에서 타입스크립트의 확장 알고리즘은 각각의 요소를 let으로 할당한 것처럼 처리함. 처음 할당된 값으로 타입을 추론하고 다른 타입으로 할당 안됨.
  • 타입스크립트의 default behavior를 오버라이드한다. 타입 선언을 하거나 타입 검사이에 부가적인 컨텍스트를 제공한다, 또 const assertion(as const)을 사용하는 방법이 있다. as const를 하면 타입스크립트는 가장 좁은 타입 중에서 타입을 추론함. no widening

item22. understand Type Narrowing

타입 축소를 이해하라

타입 확장의 반대
타입스크립트가 넓은 타입에서 좁은 타입으로 가는 과정.

  • null checking
  • instanceof
  • Array.isArray 같은 함수 사용
  • tag : tagged union or discriminated union

타입을 좁히는 방법을 이해하라
tagged, discriminated(차별화된) 유니온과 사용자 정의 타입 가드를 쓰는거는 타입을 좁히는데 도움을 준다

item23. create object all at once.

object를 한번에 생성하다.

빈 오브젝트를 선언한 후에 프로퍼티를 할당할 수 없다.

const pt = {}
pt.x = 1; //error

interface Point { x: number; y: number; };
const pt: Point = {}; //error
const pt = {x: 3, y: 4}; //ok

as (type assertion) 으로 에러를 없앨 순 있지만 한번에 생성하는 방법이 더 좋다.
...(object spread operator)를 통해 large object도 한번에 선언 가능.

요약

  • 조금씩 보단 한번에 object를 만드는 걸 선호해라, ...는 안전한 방법
  • 조건적으로 프로퍼티를 추가하는 방법을 알아라

item24. be consistent in your use of aliases

별칭 사용에 일관성을 가져라

alias
alias를 도입했으면 일관성있게 사용하라

타입스크립트의 control flow는 로컬변수에는 좋고 프로퍼티에는 안전하지 않다

요약

  • aliasing은 타입스크립트가 narrowing types하는 것을 막다. alias를 사용하면 일관적으로 써라.
  • destructuring을 사용해라
  • 함수호출이 프로퍼티의 타입 세분화(refinements)를 어떻게 무효화시킬 수 있는지, 프로퍼티보다 로컬변수의 refinement를 믿어라

item 33. Prefer More Precise Alternatives to String Types.

문자열보다 보다 정확한 대안을 선호하라

그냥 string type을 사용하는 것 보단 더 좁은 타입을 정해주는게 좋다
엄격한 타입 검사 말고도 장점이 또 있다
1. 의미가 손상되지 않는다
그냥 string으로 넘기면 Album의 정의가 숨겨져있음
2. 타입에 documentation (설명)을 첨부할 수 있다.

또 다른 string의 잘못된 사용은 함수 파라미터에 있다.
any[] 로 파라미터 타입 정하면 문제가 많다. 특히 return 값에서
string은 any와 같은 문제를 몇가지 가지고있다.
invalid 한 값을 허용하고 타입 간의 관계를 숨긴다.

1.generic type 사용
2.subset of string 을 정의하는 것

명확한 타입 선언은 에러를 잡고 가독성을 향상하는 데 좋~다~

item 34. Prefer incomplete type of inaccurate type

부정확한 타입의 불완전한 타입을 선호하라

profile
Front-end developer

0개의 댓글