inblog logo
|
Typescript Study

    [Learning Typescript] Ch.4 객체

    Typescript
    Dragon C's avatar
    Dragon C
    Nov 29, 2023
    [Learning Typescript] Ch.4 객체
    Contents
    4.1 객체 타입4.1.1 객체 타입 선언4.1.2 별칭 객체 타입4.2 구조적 타이핑4.2.1 사용 검사4.2.2 초과 속성 검사4.2.3 중첩된 객체 타입4.2.4 선택적 속성4.3 객체 타입 유니언4.3.1 유추된 객체 타입 유니언4.3.2 명시된 객체 타입 유니언4.3.3 객체 타입 내로잉4.3.3 판별된 유니언 👍🏻4.4 교차 타입4.4.1 교차 타입의 위험성

    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 타입이 등장하는 경우는 대부분 교차 타입을 잘못 사용했을 때 발생

    Share article

    Typescript Study

    RSS·Powered by Inblog