Skip to content

Latest commit

 

History

History
229 lines (192 loc) · 6.87 KB

타입호환.md

File metadata and controls

229 lines (192 loc) · 6.87 KB
1. 타입 호환성, 타입 호환성의 기준(타입이 다른 변수 간 호환성 판단 기준)
2. 구조적타이핑 덕타이핑

1. 타입 호환성, 타입 호환성의 기준(타입이 다른 변수 간 호환성 판단 기준)

다른 타입의 데이터를 서로 어떻게 할당하거나 사용할 수 있는지를 결정하는 원칙
= 같은 필드와 메서드를 가지고 있다면, 이 둘은 같은 타입으로 간주
(= 타입스크립트는 구조적 하위 유형 지정을 기반임.)

1.1 구조적 하위 유형 지정을 기반?

구성원(여기서는 name)만 비교해서 같은 타입인지 비교.
반대되는 개념인 명목적 구조에서는 'class Dog implements Pet' 라고 꼭 지정해줘야한다.
자식(Dog)는 부모(Pet)을 항상 취급할 수 있다. 자식이 슈퍼셋이기떄문에... (리스코프 치환 원칙)
interface Pet {
  name: string;
}
class Dog {
  name: string;
}
let pet: Pet;
// 타입스크립트에서는 구조적 하위 유형 지정 기반이기때문에 
// 구성원만 비교해서 호환이 가능하다는뜻이다!
pet = new Dog();
함수의 파라미터만 반공병성 -> 유틸함수 만들때 이해하기위함
나머지는 공병성

1.2 어디까지 타입 호환이 될까요?

// 1.2.1 구조적타이핑에서의 예제 (기본)
// 변수니까 공변성
interface Pet {
  name: string;
}
interface Dog {
  name: string;
  owner: string; 
}
let pet: Pet;

let dog = { name: "Lassie", owner: "Rudd Weatherwax" }; // { name: string; owner: string; }

// dog implements Pet을 하지 않아도 pet 타입에 dog값 할당 가능 => 구조적 타이핑에서 최소한의 name이라는 조건을 만족함. dog타입은 { name: string; owner: string; }로 추론됨
pet = dog;
// 1.2.2 구조적 타이핑에서의 예제 (함수 파라미터)
interface Pet {
  name: string;
}
interface Dog {
  name: string;
  owner: string; 
}
let dog = { name: "Lassie", owner: "Rudd Weatherwax" };

function greet(pet: Pet) {
  console.log("Hello, " + pet.name);
}
greet(dog); // 펫타입만 받기로했는데 dog를 넣어도됨.
// 객체 리터럴인 { name: "Lassie", owner: "Rudd Weatherwax" }를 그대로 넣으면,
// 첫 스터디 때 학습한 함수는 파라미터를 지정해줘야한다.는 규칙을 어기는것 같지만,
// 변수를 통해 프로퍼티 검사를 우회하는것임
// 주의: 알려진 타입에서만 사용 가능 (Pet은 name만 가지는데 그 이외의 변수 owner를 사용한다는건 오류가 맞음)
let dog: Pet = { name: "Lassie", owner: "Rudd Weatherwax" }; // Error
// 1.2.3 함수 파라미터, 리턴타입
// 큰바구니에 작은그릇 넣기 가능, 작은 바구니에 큰그릇 넣기 불가능

// 파라미터 유형
let x = (a: number) => 0;
let y = (b: number, s: string) => 0;
y = x; // OK
x = y; // Error

// 리턴 유형
let x = () => ({ name: "Alice" });
let y = () => ({ name: "Alice", location: "Seattle" });
x = y; // OK
y = x; // Error, because x() lacks a location property
// 1.2.4 함수 매개변수 이분산
// 매개변수의 유형을 비교할 때 소스 매개변수가 대상 매개변수에 할당 가능하거나 그 반대이면 할당이 성공
// 결론은 타입스크립트 코드를 최대한 자바스크립트 코드와 유사한 형태로 만드는 것이 좋기때문에 1번유형을 권장.
enum EventType {
  Mouse,
  Keyboard,
}
interface Event {
  timestamp: number;
}

// extends는 이미 Event를 포함하고있음.(timestamp를 포함함), implements는 구현강제
interface MyMouseEvent extends Event {
  x: number;
  y: number;
}
interface MyKeyEvent extends Event {
  keyCode: number;
}
function listenEvent(eventType: EventType, handler: (n: Event) => void) {
  /* ... */

}
// 1 MyMouseEvent는 Event를 포함하고있음 -> 가능! (바람직하지않으나 일반적이고 유용한 방법, 엄격모드에서는 안됨)
listenEvent(EventType.Mouse, (e: MyMouseEvent) => console.log(e.x + "," + e.y));

// 2 엄격모드에 맞추려고 하다보니 생긴 유형들 -> 가능하지만 가독성 안좋음
listenEvent(EventType.Mouse, (e: Event) =>
  console.log((e as MyMouseEvent).x + "," + (e as MyMouseEvent).y)
);
listenEvent(EventType.Mouse, ((e: MyMouseEvent) =>
  console.log(e.x + "," + e.y)) as (e: Event) => void);

// 3 완전히 호환되지 않는 number는 사용 불가능!
listenEvent(EventType.Mouse, (e: number) => console.log(e));
// 1.2.4 클래스

class Animal {
  feet: number;
  constructor(name: string, numFeet: number) {}
}
class Size {
  feet: number;
  constructor(numFeet: number) {}
}
let a: Animal;
let s: Size;
a = s; // OK
s = a; // OK
// 1.2.5 제네릭

// T를 가지고 아무것도 하지 않음
// Empty<number>와 Empty<string>는 구조적으로 동일함
// x=y, y=x 할당가능
interface Empty<T> {}
let x: Empty<number>;
let y: Empty<string>;
x = y; // OK, because y matches structure of x


// data 속성을 가지고있음
// data는 number or string 타입으로 구조적으로 같지 않아짐
// x != y, y !=x
interface NotEmpty<T> {
  data: T;
}
let x: NotEmpty<number>;
let y: NotEmpty<string>;
x = y; // Error, because x and y are not compatible

2. 구조적 타이핑, 덕타이핑

구조적 타이핑은 두 객체가 같은 속성과 메서드가 있다면 같은 타입으로 취급함.
같은 구조를 가진 타입이 많을때 많이사용

덕 타이핑은 “만약 어떤 새가 오리처럼 걷고, 헤엄치고, 꽥꽥거리는 소리를 낸다면, 나는 그 새를 오리라고 부를 것이다.”
라는 유명한 말처럼 기능을 제대로 수행하면 오리라고 인식하기로한것
객체가 특정 인터페이스에서 요구하는 모든 메서드와 속성을 가지고 있다면, 그 객체는 그 인터페이스를 구현하는 것으로 간주

=> 자바스크립트는 기본적으로 덕타이핑 기반으로 개발자는 타입이 뭔지 신경안씀
타입스크립트는 이를 “구조적 타이핑” 기반으로 모델링한것
// 타입 선언
interface Lion {
  age: number;
  sounds: string;
}

interface Cat {
  age: number;
  sounds: string;
  favorite: 'mouse' | 'tuna' | 'chur';
}

// 타입에 맞게 변수 선언 + 할당
const simba: Lion = {
  age: 10,
  sounds: 'roar',
};

const jerry: Cat = {
  age: 6,
  sounds: 'meow',
  favorite: 'chur'
}

function introduceLion({ age, sounds }: Lion): string {
  return `This lion is ${age} years old and sounds like ${sounds}`;
}

console.log(introduceLion(simba));
// "This lion is 10 years old and sounds like roar"

console.log(introduceLion(jerry));
// "This lion is 6 years old and sounds like meow"
// Cat 타입인 jerry 를 매개변수로 받아도 실행되는 것 확인 가능
// 필요조건이기때문에 (=반공변성이기때문에) 가능