woowacourse-study / 2022-thanks-giving-effective-typescript

🍂추석 연휴 집중🍂 이펙티브 타입스크립트를 읽는 모임 (✌️완주완료✌️)
6 stars 0 forks source link

2022.09.05 #2

Closed moonheekim0118 closed 2 years ago

moonheekim0118 commented 2 years ago

비도 오고 그래서~ 타스 생각이 났어 ~~

liswktjs commented 2 years ago

타입스크립트 서버 또한 언어 서비스를 제공한다

타입이 값들의 집합이라고 생각하기

*일단 할 게 많아서 급한 불 좀 끄고 다시 와서 정리 하겠습니당

prefer2 commented 2 years ago

타입이 값들의 집합이라고 생각하기

가장 작은 집합은 공집합이며, 타입스크립트에서는 never 타입이다.

그 다음으로 작은 집합은 한 가지 값만 포함하는 타입이다. 타입스크립트에서는 리터럴(literal) 타입이다.

두 개, 새 개로 묶으려면 유니온(union) 타입을 사용한다. 유니온 타입은 값 집합들의 합집합이다.

타입스크립트 오류 중 ‘할당 가능한'아라는 문구는 ~의 원소 또는 ~의 부분 집합 관계를 의미한다.

집합의 관점에서, 타입 체커의 주요 역할은 하나의 집합이 다른 집합의 부분 집합인지 검사하는 것이다.

interface Person {
  name: string;
}

interface Lifespan {
  birth: Date;
  death?: Date;
}

type PersonSpan = Person & Lifespan

& 연산자은 두 타입의 교집합을 계산한다. 결과값이 공집합이 나올 것으로 예상할 수 있지만 타입 연산자는 인터페이스의 속성이 아닌, 값의 집합(타입의 범위)에 적용된다. 그래서 Person과 Lifespan을 둘 다 가지는 값이 인터섹션 타입에 속하게 된다. 인터섹선 타입의 값은 각 타입 내의 속성을 모두 포함하는 것이 일반적인 규칙이다.

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

타입이 집합이라는 관점에서 extends의 의미는 ~의 부분 집합이라는 의미로 받아들일 수 있다.

function getKey<K extends string>(val: any, key: K) {

}

K는 string의 부분 집합 범위를 가지는 어떠한 타입. string 리터럴 타입, string 리터럴 타입의 유니온, string 자신을 포함한다.

타입 단언보다는 타입 선언을 사용하기

interface Person {name: string};

const alice: Person = {name: 'Alice'};
const bob = {name: 'Bob'} as Person;

두 가지 방법은 결과가 같아 보이지만 그렇지 않다. 첫번째 방법은 타입 선언, 두번째 방법은 타입 단언이다. 타입 단언은 타입스크립트가 추론한 타입이 있더라도 Person 타입으로 간주한다. 타입 단언보다 타입 선언을 사용하는게 낫다.

타입 선언은 할당되는 값이 해당 인터페이스를 만족하는지 검사한다. 타입 단언은 강제로 타입을 지정했으니 타입 체커에게 오류를 무시하라고 하는 것이다. 타입 단언이 꼭 필요한 경우가 아니라면, 안전성 체크가 되는 타입 선언을 사용하는 것이 좋다.

const bob = <Person>{} 은 {} as Person과 동일하다. 이런 형식은 리액트에서 컴포넌트 태그로 인식되기 때문에 잘 쓰이지 않는다.

interface Person {name: string};

const people = ['alice', 'bob', 'jan'].map(name => ({name})); // Person[]을 원했지만 결과는 {name: string;}[]

const people2 = ['alice', 'bob', 'jan'].map(name => ({name} as Person)); // 타입 단언

const people3 = ['alice', 'bob', 'jan'].map(name => {
  const person: Person = {name};
  return person
}); // 타입 선언

const people4 = ['alice', 'bob', 'jan'].map((name): Person => ({name})); // 타입 선언

화살표 함수의 타입 선언은 추론된 타입이 모호할 때가 있다. 타입 단언 형식보다는 타입 선언 형식으로 표현해주자

(name): Person은 name의 타입이 없고, 반환 타입이 Person이라고 명시한다. (name: Person)은 name의 타입이 Person임을 명시하고 반환 타입은 없다는 의미이기 때문에 오류가 발생한다.

타입 단언은 타입 체커가 추론한 타입보다 우리가 판단하는 타입이 더 정확할 때 의미가 있다. 모든 타입은 unknown의 서브타입이기 때문에 unknown이 포함된 단언문은 항상 동작한다.

객체 래퍼 타입 피하기

string 기본형에는 메서드가 없지만, 자바스크립트에는 메서드를 가지는 String 객체 타입이 정의되어 있다. 다른 기본형에도 동일하게 객체 래퍼 타입이 존재한다. 이 래퍼 타입들 덕분에 기본형 값에 메서드를 사용할 수 있고, 정적 메서드도 사용할 수 있다. 타입스크립트는 기본형과 객체 래퍼 타입을 별도로 모델링한다.

string은 String에 할당 할 수 있지만 String은 string에 할당할 수 없다.

moonheekim0118 commented 2 years ago

타입이 값들의 집합이라고 생각하기

type K = keyof (Person | Lifespan); // 타입이 never

타입 단언보다 타입 선언 사용하기


const people = ['alice', 'bob', 'jan'].map(name => ({name}));
// Person[] 을 원했지만 결과는 { name: string; } [] 

// 타입 단언을 쓰면
const people = ['alice', 'bob', 'jan'].map(name => ({name} as Person));

// 선언하기 1번 
const people = ['alice', 'bob', 'jan'].map(name=> {
    const person: Person = {name};
    return person;
} ); // 타입은 Person[]

// 선언하기 2번 (리팩터링)

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

선언보다 단언이 유리한 경우

const el = document.body  as Person; // 에러
const el = document.body as unknown as Person; // ㅇㅋ

객체 래퍼 타입 피하기

잉여 속성 체크의 한계 인지하기

interface Room {
  numDoors: number;
  ceilingHeightFt: number;
}

const r:Room {
  numDoors:1,
  ceilingHeightFt:10,
  elephant: 'present'
}
// 개체 리터럴은 알려진 속성만 지정할 수 있으며, Room 형식에 elephant 가 없습니다.

잉여 속성 체크가 일어나는 경우

  1. 타입이 선언되어 있을 때, 객체 리터럴로 값을 할당 할 때
  2. 타입이 선언되어 있는 매개변수에, 객체 리터럴로 매개변수를 전달할 때

잉여 속성 체크가 일어나지 않는 경우

  1. 타입 선언대신 타입 단언을 사용 할 때
  2. 타입이 선언된 값 혹은 매개변수에 객체 리터럴이 아니라 변수로 값을 할당하는 경우

잉여 속성 체크가 필요한 단적인 예시


interface Option{
  title: string;
  darkMode?:boolean;
}

const o1 : Option = document;
const o2 : Option = new HTMLAnchorElement;
// 에러 없음

함수 표현식에 타입 적용하기


type BinaryFn = (a: number, b:number)=>number;
const add: BinaryFun =(a,b)=>a+b;

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

공통점

  1. 타입 선언된 변수에 객체리터럴 할당 시 잉여속성을 체크해준다.
  2. 인덱스 시그니쳐를 사용 할 수 있다.
  3. 함수 타입도 정의 할 수 있다. (그런데 이 경우는 대부분 타입 alias 를 쓰는게 좋다.)
  4. 제네릭이 가능하다.
  5. 인터페이스는 타입을 확장할 수 있으며, 타입은 인터페이스를 확장 할 수 있다.
// 인터페이스가 타입 확장
interface IStateWithPop extends TState{
    population:number;
}

// 타입이 인터페이스 확장
type TStateWithPop = IState & { population: number;}
  1. 클래스 구현(implements) 시, 타입과 인터페이스 둘다 사용 가능

차이점

  1. 유니온 타입은 있지만 유니온 인터페이스는 없다.
  2. 인터페이스는 타입을 확장(extends) 할 수 있지만 타입은 불가능하다.

유니온 타입을 확장해야하는경우

type Input = { } ;
type Output = {} ;

inteface VariableMap {
    [name:string] : Input | Output;
}

유니온 타입에 name 속성을 붙인 타입 만들기

type NamedVariabel = (Input | Output) & { name: string } ;
  1. 튜플과 배열타입도 type 키워드로 더 간결하게 표현 가능
type Pari = [number, number];
type StringList = string[];
type NamedNums = [string, ...number[]];
  1. 인터페이스는 보강(agument) 이 가능하다. (선언 병합! declaration merging)

interface IState { name: string; capital: string; }

interface IState{ population:number; }

const newyork : IState{ name: 'NewYork', capital: 'NewYork', population: 500_00 }


- 위와 같은 선언 병합은 주로 타입 선언 파일에서 사용된다.
- 따라서 선언 합을 지원하기 위해서는 무조건 inteface 를 사용해야한다.
- 타입스크립트는 여러 버전의 자바스크립트 표준 라이브러리에서 여러 타입을 모아 병합한다. 병합을 통해서 Array 인터페이스에 여러 메서드가 추가된다.
- 따라서 프로퍼티가 추가되는 것을 원치 않는다면, 인터페이스 대신 타입을 사용해야한다.
- 

## 몰랐던 지식

```ts
inteface Example{
  [key:string] : string;
}

이거를 인덱스 시그니처라고 한다.

soyi47 commented 2 years ago

아이템 6. 편집기를 사용하여 타입 시스템 탐색하기

아이템 7. 타입이 값들의 집합이라고 생각하기

아이템 8. 타입 공간과 값 공간의 심벌 구분하기

아이템 9. 타입 단언보다는 타입 선언을 사용하기

아이템 10. 객체 래퍼 타입 피하기

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

interface Options {
  title: string;
  darkMode?: boolean;
}

const o1: Options = {
  title: 'Spider Solitaire';
  darkMode: true;
  elephant: 'present',
} // 에러! 개체 리터럴은 알려진 속성만 지정할 수 있으며 'Options ' 형식에 'elephant'가 없습니다.
// => 잉여 속성 체크

const o2: Options = { title: 'Ski Free', darkmode: true };
// 'Options' 형식에 'darkmode'이(가) 없습니다.
// => 잉여 속성 체크

const o3: Options = document; // 정상 <= string 타입인 title 속성을 가지기 때문
const o4: Options = new HTMLAnchorElement; // 정상 <= string 타입인 title 속성을 가지기 때문

const intermediate = { title: 'Ski Free', darkmode: true };
const o5: Options = intermediate; 
// 정상! 
// intermediate이 객체 리터럴이 아니기 때문에 잉여 속성 체크가 적용되지 않는다.

const o6 = { title: 'Ski Free', darkmode: true } as Options;
// 정상!
// 타입 단언문을 사용할 때 잉여 속성 체크가 적용되지 않는다.

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