1.객체 형태를 설명하는 방법
2. 타입스크립트가 객체의 할당 가능성을 확인하는 방법
4.1 객체 타입
3장 리터럴 타입
구체적인 버전의 원시 타입
const philosopher = "Hypatia";
원시 타입 값 (string, number,…)중 어떤 것이 아닌 특정 원싯값
객체 리터럴
각자의 타입이 있는 키와 값의 집합
객체 타입
{…}
구문을 사용하여 객체 리터럴 생성객체의 값과 동일한 속성명과 원시 타입을 가짐
value.멤버
또는value['멤버']
로 속성 접근
ex) 다른 속성의 이름으로 접근할 시 오류
const poet = {
born: 1935,
name: "Mary Oliver",
};
poet['born']; // 타입: number
poet.name; // 타입: string
poet.end;
// ~~~
// Error: Property 'end' does not exist on type '{ born: number; name: string; }
poet.name = 'mcy'; // Ok
4.1.1 객체 타입 선언
객체의 타입을 명시적으로 선언하기 위해 사용 필드 값 대신 타입을 사용해 설명
let poetLater: {
born: number;
name: string;
}
// ok
poetLater = {
born: 1935,
name: "Mary Oliver",
};
poetLater = "Sappho";
// ~~~~~~
// Error: Type 'string' is not assignable to type '{ born: number; name: string; }'
4.1.2 별칭 객체 타입
같은 객체 타입 중복 작성을 쉽게
오류 메시지를 좀 더 직관적으로 읽기 쉽게
type Poet = {
born: number;
name: string;
};
let poetLater: Poet;
// Ok
poetLater = {
born: 1935,
name: "Sara Teasdale",
};
poetLater = "Emily Dickinson";
//~~~~~~~
// Error: Type 'string' is not assignable to 'Poet'.
💡
대부분의 타입스크립트 프로젝트는 객체 타입을 설명할 때 interface 키워드를 선호 (7장)
4.2 구조적 타이핑
타입시스템
구조적으로 타입화 되어 있음
타입을 충족하는 모든 값을 해당 타입의 값으로 사용 가능
type WithFirstName = {
firstName: string;
};
type WithLastName = {
lastName: string;
};
const hasBoth = {
firstName: "Lucille",
lastName: "Clifton",
};
// Ok: 'hasBoth'는 'string' 타입의 'firstName'을 포함함
let withFirstName: WithFirstName = hasBoth;
// Ok: 'hasBoth'는 'string' 타입의 'lastName'을 포함함
let withLastName: WithLastName = hasBoth;
console.log('withLastName: ', withLastName);
// withFirstName: {
// firstName: "Lucille",
// lastName: "Clifton"
// }
💡
구조적 타이핑과 덕 타이핑 차이 - 타입스크립트 (구조적 타이핑) : 정적 시스템이 타입을 검사 - 자바스크립트 (덕 타이핑) : 동적 타이핑의 한 종류. 런타임에서 사용될 때까지 객체 타입을 검사하지 않음 * 덕타이핑: 오리처럼 보이고 오리처럼 꽥꽥거리면, 오리일 것이다.
4.2.1 사용 검사
값을 해당 객체 타입에 할당할 수 있는지 확인
ex) 객체 타입에 필요한 멤버가 없는 경우
type FirstAndLastNames = { first: string; last: string; }; // 0k const hasBoth: FirstAndLastNames = { first: "Sarojini", last: "Naidu", }; const hasOnlyOne: FirstAndLastNames = { //~~~~~~~~~~ // Error: Property 'last' is missing in type '{ first: string; }' // but required in type 'FirstAndLastNames'. first: "Sappho" };
ex) 타입이 일치하지 않는 경우
type TimeRange = { start: Date; }; const hasStartString: TimeRange = { start: "1879-02-13", //~~~ // Error: Type 'string' is not assignable to type 'Date'. };
4.2.2 초과 속성 검사
타입검사기가 해당 타입에 예상되는 필드만 있는지 확인
ex) 객체 타입에서 정의된 것보다 많은 필드가 있는 경우
type Poet = { born: number; name: string; } // Ok: Poet 의 필드와 일치함 const poetMatch: Poet { born: 1928, name: "Maya Angelou" }; const extraProperty: Poet = { activity: "walking", //~~~~~~~~~~~~~ // Error: Type '{activity: string; born: number; name: string; }' // is not assignable to type 'Poet'. // Object literal may only specify known properties, // and 'activity' does not exist in type 'Poet'. born: 1935, name: "Mary Oliver", };
초과 속성 검사는 객체 타입으로 선언된 위치에서 생성되는 객체 리터럴에 대해서만 일어남
const existingObject = { activity: "walking", born: 1935, name: "Mary Oliver", }; const extraPropertyButOk: Poet = existingObject; // Ok
4.2.3 중첩된 객체 타입
객체 내 다른 객체의 멤버로 중첩 가능
위에서 설명한 객체 타입에 대한 동일한 규칙 적용
type Poem = { author: { firstName: string; lastName: string; }; name: string; }; // Ok const poemMatch: Poem = { author: { firstName: "Sylvia", lastName: "Plath", }, name: "Lady Lazarus", }; const poemMismatch: Poem = { author: { name: "Sylvia Plath", // ~~~~~~~~~~~~~~~~~~~ // Error: Type '{ name: string; }' is not assignable // to type '{ firstName: string; lastName: string; }'. // Object literal may only specify known properties, and 'name' // does not exist in type '{ firstName: string; lastName: string; }'. }, name: "Tulips", };
4.2.4 선택적 속성
:
앞에?
를 추가하여 선택적 속성 정의 가능
type Book = {
author?: string;
pages: number;
};
// 0k
const ok: Book = {
author: "Rita Dove",
pages: 80,
};
const missing: Book = {
// ~~~~~~~
// Error: Property 'pages' is missing in type
// '{ pages: number; }' but required in type 'Book'.
author: "Rita Dove",
};
선택적 속성(
?
): 있거나 없어도 됨필수 선언된 속성 | undefined : undefined 일지라도 반드시 있어야함
type Writers = {
author: string | undefined;
editor?: string;
};
// Ok: author는 undefined으로 제공됨
const hasRequired: Writers = {
author: undefined,
};
const missingRequired: Writers = {};
// ~~~~~~~~~~~~~~~~~~
// Error: Property 'author' is missing in type '{}' but required in type 'Writers'.
4.3 객체 타입 유니언
하나 이상의 서로 다른 객체 타입을 정의하려고 할 때
속성값을 기반으로 해당 객체 타입 간에 타입을 좁혀야 할 때
4.3.1 유추된 객체 타입 유니언
여러 객체 타입 중 하나가 될 수 있는 초깃값이 주어질 때 객체 타입 유니언으로 유추
ex) name 은 필수 / pages 와 rhymes 는 있을 수도, 없을 수도 있음
const poem = Math.random() > 0.5
? { name: "The Double Image", pages: 7 }
: { name: "Her Kind", rhymes: true };
// 타입:
// {
// name: string;
// pages: number;
// rhymes?: undefined;
// }
// |
// {
// name: string;
// pages?: undefined;
// rhymes: boolean;
// }
poem.name; // string
poem.pages; // number | undefined
poem.rhymes; // booleans | undefined
4.3.2 명시된 객체 타입 유니언
단점
코드를 더 상세하게 작성
장점
객체 타입을 더 많이 제어 가능
ex) name 은 필수 / pages 와 rhymes 는 항상 존재한다는 보장 없음
type PoemWithPages = {
name: string;
pages: number;
};
type PoemWithRhymes = {
name: string;
rhymes: boolean;
};
type Poem PoemWithPages | PoemWithRhymes;
const poem: Poem = Math.random() > 0.5
? { name: "The Double Image", pages: 7 }
: { name: "Her Kind", rhymes: true };
poem.name; // Ok
poem.pages;
// ~~~~~
// Error: Property 'pages' does not exist on type 'Poem'.
// Property 'pages' does not exist on type 'PoemWithRhymes'.
poem.rhymes;
// ~~~~~
// Error: Property 'rhymes' does not exist on type 'Poem'.
// Property 'rhymes' does not exist on type 'PoemWithPages'.
4.3.3 객체 타입 내로잉
객체 타입 유니언도 유니언 타입처럼 타입을 좁혀야함.
if ("pages" in poem) { poem.pages; // Ok: poem은 PoemWithPages로 좁혀짐 } else { poem.rhymes; // Ok: poem은 PoemWithRhymes로 좁혀짐 }
존재하지 않는 객체의 속성에 접근하려고 하면 타입 오류 발생함
if (poem.pages) { /* ... */ } // ~~~~~~~~~~~~~~ // Error: Property 'pages' does not exist on type 'PoemWithPages | PoemWithRhymes'. // Property 'pages' does not exist on type 'PoemWithRhymes'.
4.3.3 판별된 유니언 👍🏻
객체의 속성이 객체의 형태를 나타내도록 하는 타입 형태
판별값 - 객체의 타입을 가리키는 속성
type PoemWithPages = {
name: string;
pages: number;
type: 'pages';
};
type PoemWithRhymes = {
name: string;
rhymes: boolean;
type: 'rhymes';
};
type Poem = PoemWithPages | PoemWithRhymes;
const poem: Poem = Math.random() > 0.5
? { name: "The Double Image", pages: 7, type: "pages" }
: { name: "Her Kind", rhymes: true, type: "rhymes" };
if (poem.type === "pages") {
console.log('It's got pages: ${poem.pages}'); // Ok
} else {
console.log('It rhymes: ${poem. rhymes}');
}
poem.type; // 타입: 'pages' ¦ 'rhymes'
poem.pages;
// ~~~~~
// Error: Property 'pages' does not exist on type 'Poem'.
// Property 'pages' does not exist on type 'PoemWithRhymes'.
4.4 교차 타입
교차 타입(&) - 여러 기존 객체 타입을 별칭 객체 타입으로 결합해 새로운 타입을 생성
type Artwork = {
genre: string;
name: string;
}
type Writing = {
pages: number;
name: string;
}
type WrittenArt = Artwork & Writing;
// 다음과 같음:
// {
// genre: string;
// name: string;
// pages: number;
// }
ex) 교차 타입과 유니언 타입을 결합한 형태
type ShortPoem = { author: string } & (
¦ {kigo: string; type: "haiku"; }
¦ { meter: number; type: "villanelle"; }
);
// Ok
const morningGlory: Short Poem = {
author: "Fukuda Chiyo-ni",
kigo: "Morning Glory",
type: "haiku",
};
// 객체 타입에 필요한 멤버가 없음 (사용 검사)
const oneArt: Short Poem = {
// Error: Type '{ author: string; type: "villanelle"; }'
// is not assignable to type 'Short Poem'.
// Type { author: string; type: "villanelle"; }' is not assignable to
// type { author: string; } & { meter: number; type: "villanelle"; }'.
// Property 'meter' is missing in type '{ author: string; type: "villanelle"; }'
// but required in type '{ meter: number; type: "villanelle"; }'.
author: "Elizabeth Bishop",
type: "villanelle",
};
4.4.1 교차 타입의 위험성
긴 할당 가능성 오류
유니언 타입과 결합하는 것처럼 복잡한 교차 타입을 만들수록 오류 메시지를 읽기 어려워짐
ex) 별칭 객체 타입 설정
type ShortPoemBase = { author: string }; type Haiku = ShortPoemBase & { kigo: string; type: "haiku" }; type Villanelle = Short PoemBase & { meter: number; type: "villanelle" }; type ShortPoem = Haiku | Villanelle; const oneArt: ShortPoem = { // ~~~~~ // Error: Type '{ author: string; type: "villanelle"; }' // is not assignable to type/Short Poem'. // Type '{ author: string; type: "villanelle"; }' is not assignable to type 'Villanelle'. // Property 'meter' is missing in type '{ author: string; type: "villanelle"; }' // but required in type '{ meter: number; type: "villanelle"; }'. author: "Elizabeth Bishop", type: "villanelle", };
never
교차 타입은 잘못 사용하기 쉽고 불가능한 타입을 생성함
ex) 원시 타입은 동시에 여러 타입이 될 수 없음
type NotPossible = number & string; // 타입: never
never
없는 값의 집합
flow
의bottom type
uninhabitable type
let notNumber: NotPossible = 0;
// ~~~~~
// Error: Type 'number' is not assignable to type 'never';
let notString: NotPossible = "";
// ~~~~~
// Error: Type 'string' is not assignable to type 'never';
never 타입이 등장하는 경우는 대부분 교차 타입을 잘못 사용했을 때 발생