Jisoo.

기술블로그

3. 함수 및 객체의 타입과 제너릭


썸네일

함수 및 객체의 타입

타입스크립트의 함수 및 객체의 타입에 대해서 알아봅시다.


💡 함수

- 함수 표현식

타입스크립트에서 화살표 함수와 문법적으로 유사하게 함수를 표현할 수 있습니다.

1// 문자열 타입 매개변수 a를 가진 반환값이 없는 함수 사용 2function greeter(fn: (a: string) => void) { 3 fn("Hello, World"); 4}

- 선택적 매개변수

?를 사용하여 특정 매개변수를 선택적으로 만들 수 있습니다.

1function fn(x? : number) {} 2 3fn(); 4fn(100);

- 나머지 매개변수

나머지 매개변수는 스프레드 연산자 ...를 사용하여 정의할 수 있습니다.

1function multiply(n: number, ...m: number[]) { 2 return m.map((x) => n * x); 3} 4 5const a = multiply(10, 1, 2, 3, 4);

- 호출 시그니처

호출 가능한 어떤 타입을 선언할 때, 호출 시그니처를 사용할 수 있습니다.

1type DescribableFunction = { 2 description: string; 3 (someArg: number): boolean; 4}; 5 6// fn(6)으로 호출 가능 7function doSomething(fn: DescribableFunction) { 8 console.log(fn.description + " returned " + fn(6)); 9}

- this 타입 선언하기

자바스크립트에서 this는 어떻게 선언되느냐에 따라 값이 달라질 수 있습니다.

function 키워드를 이용하여 함수를 선언할 경우 this는 해당 함수의 컨텍스트를 가리킵니다.

그러나 화살표 함수로 선언할 경우 this는 글로벌 컨텍스트를 가리키게 되어 에러가 발생합니다.

1// function keyword 2const user = { 3 id: 123, 4 admin: false, 5 // this는 현재의 user 객체를 가리킵니다. 6 becomeAdmin: function () { 7 this.admin = true; 8 }, 9}; 10 11user.becomeAdmin(); // false 12 13-------------------------------------------- 14 15// arrow keyword 16const user = { 17 id: 123, 18 admin: false, 19 // this는 globalThis 20 becomeAdmin: () => { 21 this.admin = true; 22 } 23}; 24 25user.becomeAdmin(); -> Error

이를 이용하여 타입스크립트에서 this의 타입을 고정할 경우 function을 사용할 수 있습니다.

1interface User { 2 admin: boolean; 3} 4 5interface DB { 6 filterUsers(filter: (this: User) => boolean): User[]; 7} 8 9const db: DB = getDB(); 10// this는 User 타입을 갖도록 설정됩니다. 11const admins = db.filterUsers(function (this: User) { 12 return this.admin; 13});

💡 제너릭

동일한 동작을 수행하지만 매개변수의 타입이 다른 경우, 함수 오버로딩을 통해 다양한 매개변수 타입의 동일한 함수를 호출할 수 있습니다.

타입스크립트의 함수 오버로딩을 구현하기 위해서는 구현부가 모든 오버로드의 시그니처를 포함하는 타입이어야 합니다.

1function myPrint(x: string): string; 2function myPrint(x: boolean): boolean; 3function myPrint(x: number): number; 4function myPrint(x: string | boolean | number) { 5 return x; 6} 7 8const a = myPrint("hi"); // string 9const b = myPrint(1); // number 10const c = myPrint(false); // boolean

이러한 방식은 해당 함수의 반환 타입을 명확히 할 수 있다는 장점이 있으나 모든 타입에 대한 경우의 수를 모두 작성해야한다는 단점을 가지고 있습니다.

제너릭을 사용하면 이러한 문제를 간단히 해결할 수 있습니다.

1function myPrint<T>(x : T) : T { 2 return x; 3}

제너릭은 호출 시 매개변수의 타입을 결정하는 기능으로 <T> 형태로 선언할 수 있습니다.

T는 타입의 별명으로 타입스크립트가 해당 함수 실행 시 자동으로 해당 타입을 추론합니다.

- 타입 별칭에서의 제너릭

타입 별칭, 인터페이스에서도 제너릭을 사용할 수 있습니다.

1type Box<T> = { 2 content : T 3} 4 5const a : Box<string> = { 6 content : 'gdgd' 7}

- 제너릭 상속

제너릭은 인터페이스를 상속하여 특정 프로퍼티를 제한할 수 있습니다.

1interface Lengthwise { 2 length: number; 3} 4 5function loggingIdentity<Type extends Lengthwise>(arg: Type): Type { 6 console.log(arg.length); 7 return arg; 8}

- 제너릭 분산

제너릭은 유니언 타입을 만나면 분산적으로 동작합니다.

1type ToArray<Type> = Type extends any ? Type[] : never; 2 3type StrArrOrNumArr = ToArray<string | number>; // string[] | number[]

-조건부 타입 / infer

타입스크립트에서 조건부로 타입을 유추할 수 있습니다.

1type MyType<T> = T extends SomeType ? InferredType : OtherType;

infer 키워드는 타입을 추론할 때 사용하는 키워드입니다.

1// 함수의 반환 타입 추론 2type ReturnType<T> = T extends (...args: any[]) => infer R ? R : never; 3 4// 함수의 인자 타입 추론 5type FirstArgument<T> = T extends (arg: infer A) => any ? A : never; 6 7// 배열의 요소 타입 추론 8type ElementType<T> = T extends (infer U)[] ? U : never;

💡 객체

- 프로퍼티 검사

자바스크립트의 경우 객체에 해당하지 않는 프로퍼티에 접근하더라도 에러를 발생하지 않고 조용히 실패합니다.

타입스크립트는 객체의 프로퍼티를 검사하여 해당하지 않는 프로퍼티가 존재할 경우 에러를 발생시킵니다.

1class Shape{} 2 3interface PaintOptions { 4 shape: Shape; 5 xPos?: number; 6 yPos?: number; 7 readonly key : string; 8} 9 10const a : PaintOptions = { 11 shape: new Shape, 12 key: "" 13} 14 15a.zPos = 10; -> Error

- 인덱스 시그니처

인덱스 시그니처는 프로퍼티의 이름을 모르지만, 타입을 알고 있을 때 유용한 방식입니다.

프로퍼티를 [ ]로 감싸면 인덱스 시그니처를 선언할 수 있습니다.

1interface indexSigniture { 2 // 모든 프로퍼티 이름은 문자열이고 값은 숫자인 인덱스 시그니처 선언 3 [ index : string ] : number; 4 5 length : number; 6 name : string; -> Error 7}

- 구성 시그니처

객체를 생성하는 타입이 존재할 경우 구성 시그니처를 사용할 수 있습니다.

호출 시그니처에 new 연산자를 붙여 새로운 객체를 생성합니다.

1type SomeConstructor = { 2 new (s: string): SomeObject; 3}; 4 5function fn(ctor: SomeConstructor) { 6 return new ctor("hello"); 7}

- keyof 연산자

keyof 연산자는 객체의 모든 키를 합친 리터럴 유니온을 생성합니다.

1type Point = { x: number; y: number }; 2type P = keyof Point; // "x" | "y"

타입이 number, string 인덱스 시그니처를 가진 경우 keyof는 다음과 같습니다.

1type Arrayish = { [n: number]: unknown }; 2type A = keyof Arrayish; // number 3 4type Mapish = { [k: string]: boolean }; 5type M = keyof Mapish; // string | number

- 인덱싱 타입

타입의 특정 프로퍼티를 찾기 위해서 인덱싱을 사용하여 접근할 수 있습니다.

1type Person = { age: number; name: string; alive: boolean }; 2 3type I1 = Person["age" | "name"]; // string | number 4type I2 = Person[keyof Person]; // string | number | boolean 5 6// 존재하지 않는 프로퍼티 접근 시 에러 7type I3 = Person["alve"]; -> Error

배열의 임의의 타입을 number로 인덱싱하여 요소의 타입을 가져올 수 있습니다.

1const MyArray = [ 2 { name: "Alice", age: 15 }, 3 { name: "Bob", age: 23 }, 4 { name: "Eve", age: 38 }, 5]; 6 7// 배열 요소의 타입 가져오기 8type Person = (typeof MyArray)[number];

let, const 로 선언한 값은 인덱싱에 사용할 수 없습니다.

1const MyArray = [ 2 { name: "Alice", age: 15 }, 3 { name: "Bob", age: 23 }, 4 { name: "Eve", age: 38 }, 5]; 6 7type Person = (typeof MyArray)[number]; 8 9let key = "age"; 10 11// 값은 인덱싱에 사용할 수 없습니다. 12type Age = Person[key]; -> Error

- Mapped Type

Mapped Type은 어떤 타입의 프로퍼티를 바탕으로 새로운 타입을 생성하는 타입입니다.

1type OptionsFlags<Type> = { 2 // 특정 타입의 프로퍼티를 모두 순회하며 boolean 타입으로 변경 3 [Property in keyof Type]: boolean; 4}; 5 6type FeatureFlags = { 7 darkMode: () => void; 8 newUserProfile: () => void; 9}; 10 11type FeatureOptions = OptionsFlags<FeatureFlags>; 12// FeatureOptions { 13// darkMode : boolean; 14// newUserProfile : boolean; 15// }

💡 마무리

타입스크립트의 함수, 객체의 타입에 대해서 알아보았습니다.

다음에는 Utility Type에 대해 알아보겠습니다.

2. Type

1


Typescript 시리즈의 다른 포스트

썸네일-0

2. Type

타입스크립트의 타입에 대해 알아봅시다.


2024년 11월 08일

썸네일-1

1. Typescript는 무엇인가

타입스크립트에 대해서 알아봅시다.


2024년 10월 23일

관련 포스트

썸네일-0

2. 변수와 자료형

식별자와 자료형에 대해서 알아봅시다.


2024년 10월 16일

썸네일-1

1. Javascript 특징

자바스크립트 특징에 대해 알아봅시다.


2024년 10월 16일