interface Animal {
bark: true;
fur: true;
}
const dog: Animal = {
bark: true,
fur: true,
wing: false
// ~~~~~~~~~~~ 객체 리터럴은 알려진 속석만 지정 할 수 있으며, "Animal" 형식에
// (wing) 속성이 없습니다.
}
- 타입이 명시된 변수에 객체 리터럴을 할당 할 때,
1) 타입스크립트는 해당 타입의 속성이 있는지
2) 그 외의 속성은 없는지 확인 한다.
interface Animal {
bark: true;
fur: true;
}
const dog = {
bark: true,
fur: true,
wing: false
}
const d: Animal = dog;
// dog 객체는 Animal의 부분집합을 포함하고 있기 때문에, 구조적 타이핑 관점으로 할당이 가능하다.
즉, 타입스크립트의 잉여타입 검사
와 할당 가능 검사
는 다르다는 것을 알 수 있다.
bark
속성을 brak
으로 잘못 기입 하였을 때, 이를 검사 할 수 있다는 이점이 있다. ('엄격한 객체 리터럴 체크' 라고도 불림)잉여타입 검사는 1) 타입지정과 함께 객체 변수를 선언 할때
2) 함수의 매개변수로 리터럴 객체를 넣을 때
이루어진다.
잉여타입 검사를 회피하기 위해서는 1) 임시변수를 사용해 객체를 할당 하는 것
2) 인덱스 시그니처를 사용하여 타입스크립트가 추가적인 속성을 예상 하도록 하기
interface Animal {
bark: true;
fur: true;
[other: string]: unknown;
}
const dog: Animal = {
bark: true,
fur: true,
wing: false
} // 정상
// 함수 선언식
function addFunction = (a: number, b: number) => a + b;
function subFunction = (a: number, b: number) => a - b;
function mulFunction = (a: number, b: number) => a * b;
function divFunction = (a: number, b: number) => a / b;
// -------------
// 함수 표현식
type BinaryFunction = (a: number, b: number) => number;
const addFunction: BinaryFunction = (a, b) => a + b;
const subFunction: BinaryFunction = (a, b) => a - b;
const mulFunction: BinaryFunction = (a, b) => a * b;
const divFunction: BinaryFunction = (a, b) => a / b;
2) 함수의 매개변수에 타입을 선언 하는 것 보다, 함수 표현식 전체 타입을 정의 하는 것이 코드도 간결하고 안전하다. return 값에 대한 오류 까지 찾아줌
3) 다른 함수의 시그니처와 동일한 타입을 가지는 새 함수를 작성 하거나, 동일한 타입 시그니처를 가지는 여러개의 함수를 작성 할 때는 매개변수의 타입과 반환 타입을 반복해서 작성하지 말고 함수 전체의 타입 선언을 적용 해야한다.
async function checkStatusFetch(input: RequestInfo, init?: RequestInit) {
const res = await fetch(input, init);
if (!res.ok) {
throw new Error("오류 발생" + res.status);
}
return res;
}
const checkStatusFetch: typeof fetch = (input, init) => {
const res = await fetch(input, init);
if (!res.ok) {
throw new Error("오류 발생" + res.status);
// return new Error(res.status); - fetch 함수의 반환 타입을 보장 하기 때문에 return 으로 오류를 반환 하면 타입 오류가 나온다.
}
return res;
}
Type
은 union, intersection, 조건부 타입
등 복잡한 타입 구조를 가진다.Interface
는, 보강(augment)
이 가능하다. interface Person {
name: string;
age: number;
}
interface Person {
gender: "F" | "M";
}
const Peter: Person = {
name: "Peter",
age: 25,
gender: "M"
} // 오류 없음
해당 예제 처럼 속성을 확장 하는 것을 선언 병합이라 한다.
그래서 Type
/ Interface
중 어떤 것을 사용 해야하는 가?
Type
을 사용하는 것이 이점이 있음Type
이면 일관된 Type
을 사용 하기Interface
이면 일관된 Interface
을 사용 하기Interface
를 사용 하는 것이 이점이 있음DRY(Don't Repeat Yourself)
원칙을 지켜야한다.// 수정 전
function getUser(url: string, options: Options): Promise<Response> { ... }
function postUser(url: string, options: Options): Promise<Response> { ... }
// 수정 후
type HttpFunction = (url: string, options: Options) => Promise<Response>;
const getUser: HttpFunction = (url, options) => { ... }
const postUser: HttpFunction = (url, options) => { ... }
interface, extends
| type, &
을 이용해서 반복을 제거 할 수 있다.interface Person {
name: string;
family: string;
}
interface PersonWithAge extends Person {
age: number;
}
type PersonWithMBTI = Person & { MBTI: string }
// 수정 전
interface HomeState {
id: string;
title: string;
contents: string[];
}
interface NavProps {
title: string;
contents: string[];
}
/*
HomeState의 부분 집합으로 Nav의 상태를 결정 해주는 것이 바람직 함
전체 어플리케이션의 상태를 하나의 인터페이스(타입) 으로 유지 할 수 있게 해준다
*/
// 수정 후
interface NavProps {
title: HomeState["title"];
contents: HomeState["contents"];
}
/*
매핑된 타입을 이용 하면 코드의 양을 더 줄일 수 있다.
*/
interface NavProps {
[k in "title" | "contents"]: HomeState[k];
}
/*
type Pick<T, K> = { [k in K]: T[k] };
*/
type NavProps = Pick<HomeState, "title" | "contents">;
Pick<T, K extends keyof T> = { [k in K]: T[k] };
string | number | symbol
이 되어야 한다K extends keyof T
를 사용Partial<T> = { [k in keyof T]?: T[k] };
ReturnType<typeof Function>
: 함수의 반환 값에 대한 타입Type 에 속성을 추가 할 때, 속성 추가에 따른 함수를 고쳐야 하는 경우가 있다. 이를 해결 하기 위해서는 ("매핑된 타입")을 사용 하는 것이 좋다.
("매핑된 타입")을 타입으로 하는 (객체 or key들의 배열)를 조건문에 넣어 타입스크립트가 타입 체크를 할 수 있도록 하여, Type 속성 추가 및 수정에 따른 함수 변경이 강제 될 수 있도록 해야 한다.
type Props = {
age: number;
gender: "F" | "M";
name: string;
}
const CHECK_UPDATE: {[k in keyof Props]: boolean} = {
age: true,
gender: true,
name: true,
}
function checkUpdate(oldProps: Props, newProps: Props) {
let key: keyof Props;
for (key in oldProps) {
if (oldProps[key] !== newProps[key] && CHECK_UPDATE[k]) {
return true;
}
}
return false;
}
Props
에 속성을 수정 하여도 타입 체크가 되도록 하는 것이 중요하다
interface Vector {
x: number;
y: number;
}
function add(a: Vector, b: Vector) {
return {x: a.x + b.x, y: a.y + b.y };
}
// 이 경우, return Type 은 { x: number, y: number } 로 추론이 된다.
// 이를 해결 하기 위해서 함수에 대한 반환타입을 명시 해주는 것이 중요하다.