타입스크립트 공식문서

김장남·2024년 4월 12일
0

스터디

목록 보기
1/1

1. 기본타입

1.1. string, number, boolean

1.1.1. string

문자열을 나타내는 타입

const a: string = "string";

1.1.2. number

숫자 타입, javascript는 int 또는 float의 구분이 없고 모두 number타입.

const a: number = 42;
const b: number = 4.2;

1.1.3. boolean

참(true)과 거짓(false)을 나타내는 타입

const t: boolean = true;
const f: boolean = false;

1.2. Arrays

배열을 나타내는 타입 number[], string[] 또는 Array<number>,Array<string>으로 사용합니다.

const a1: number[] = [1, 2, 3, 4];
const a2: Array<number> = [1, 2, 3, 4];
const b1: string[] = ["1", "2", "3", "4"];
const b2: Array<string> = ["1", "2", "3", "4"];

1.3. any

any 타입은 해당 값에 대해 타입체크를 원하지 않을때 사용합니다.

const a: any = { test1: "hello world" };

// 어떤 타입에러도 발생하지 않습니다.
a.test1();
a();
a.bar = 100;
a = "hello world";

대부분의 경우 사용하지 않는것을 권장하지만 종종 필요한 경우가 있다고 합니다.

1.4. Type Annotation on Variable

변수에 타입을 지정할때는 앞서 예시코드에서와 같이 변수명 뒤에 : TYPE 을 붙인다.

const a: number = 1;
const b: string = "text";
const c: boolean = false;

1.5. Functions

함수에서의 타입은 parameter type과 return type이 있다.

1.5.1. Parameter type annotation

파라메터의 뒤에 타입을 지정해준다.

function greet(name: string) {
  console.log(`안녕하세요 ${name}`);
}

const greet = (name: string) => {
  console.log(`안녕하세요 ${name}`);
};

1.5.2. Return type annotation

return type은 parameter선언 뒤에 지정해줍니다.

function getDevServerPort(): number {
  return 3000;
}

const getDevServerPort = (): number => {
  return 3000;
};
// 또는 아래와같이 나타낼 수 있지만. 가독성이 좋지 않다.
//  변수명[:  함수 타입 annotation  ]
const a: (name: string) => number = (name) => {
  return 10;
};

// 대신 아래와 같은 방식으로 type 정의를 먼저 한 후에 타입을 사용하는 방식을 사용
type AFunction = (name: string) => number;

const a: AFunction = (name) => {
  return 10;
};

1.5.3. Anonymous Functions

사용은 하고 있지만 정확히 설명하기가 어려움(링크 참조)
익명함수에서는 타입스크립트가 타입을 유추(infer) 할 수 있는 경우에 유추를 해준다고 함.

const names = ["Alice", "Bob", "Eve"];

// names가 string[]타입이므로 forEach에서 s는 string으로 유추가 된다.
names.forEach(function (s) {
  console.log(s.toUpperCase());
});

// 화살표함수에서도 마찬가지로 유추가 적용된다.
names.forEach((s) => {
  console.log(s.toUpperCase());
});

ts공홈에서는 이것을 문맥적 타이핑(contextual typing)이라고 한다.

1.6. Object Types

react에서 컴포넌트의 props가 Object 타입의 흔한 예
optional은 아래의 y와 같이 표시한다.

function Sum(props: { x: number; y?: number }) {
  return <p>result: {props.x + props.y}</p>;
}

1.7. Union Types

1.7.1. Union Type 정의 하기

아래 printId 함수는 number 또는 string 타입인 id를 parameter로 받는다. 이것을 코드로 나타내면 아래와 같다.
이 때 string타입에만 유효한 메서드는 사용 할 수 없다.

function printId(id: number | string) {
  console.log(id.toUpperCase());
  /**            ^
   *  Property 'toUpperCase' does not exist on type 'string | number'.
   *   Property 'toUpperCase' does not exist on type 'number'.
   **/
}

1.7.2. Union Type 타입 좁히기

위와같은 문제를 해결 하기 위해선 javascript에서와 마찬가지로 코드를 사용 하여 유니온 타입을 좁혀야 합니다.

function printId(id: number | string) {
  if (typeof id === "string") {
    console.log(id.toUpperCase());
  } else {
    console.log(id);
  }
}

또는 다른 예로

function welcomePeople(x: string[] | string) {
  if (Array.isArray(x)) {
    // 'x' is 'string[]'
    console.log(`Welcome ${x.join(" and ")}`);
  } else {
    // 'x' is 'string'
    console.log(`Welcome ${x}`);
  }
}

else 구문에서 따로 타입을 확인하지 않아도 if문에서 좁혀진 타입으로 나머지 타입이 정해집니다.

그리고 Union Type에서 두 타입이 공통적으로 갖고 있는 메서드는 문제 없이 호출 가능합니다.

function getLength(x: number[] | string) {
  return x.length();
}

1.2. 읽기 전용 속성

readonly 속성은 아래와 같이 표현한다.

interface User {
  readonly id: number;
}

1.3. 선택적 속성

optional한 속성은 아래와 같이 표현한다.

interface User {
  hasHobby?: boolean;
}

2. Type vs Interface

2.1. 차이, 각각 언제 사용하는지

2.1.1. 차이

2.1.1.1. 선언

type Animal = {
  name: string;
};

interface Animal {
  name: string;
}

2.1.1.2. 확장

type Bear = Animal & {
  honey: boolean;
};

interface Bear extends Animal {
  honey: boolean;
}

2.1.1.3. vscode에서 마우스를 타입 위에 올려두었을때의 차이!

type은 마우스를 올려놓으면 미리보기가 타입의 세부내용이 보이지만 interface는 마우스를 올려놓으면 타입의 상세내용이 보이지 않습니다. 다만 interface도 mac기준 cd키를 누르고 마우스오버를 하면 타입 상세가 보입니다.
(참고: 우아한 기술블로그)

2.1.1.4. 선언 병합

interface는 선언후에도 같은 이름으로 선언하면 하면 자동으로 타입이 병합됩니다.

interface IShape {
  width: number;
  height: number;
}

interface IShape {
  scale: number;
}

const shape: IShape = {
    width: 10;
    height: 20;
    scale: 0.2;
}

하지만 타입의 경우에는 같은 이름으로 선언 하게 되면 식별자 중복 에러가 발생합니다.

interface TShape {
  width: number;
  height: number;
}

// Error: Duplicate identifier 'TShape'.
interface TShape {
  scale: number;
}

interface의 선언병합이 쉽기 때문에 장점이될 수 도 있지만, 의도치 않은 선언병합이 발생할 수 있다는 점은 단점으로 작용하기도 합니다. (협업을 하는 경우 동료가 내가 만든 interface를 의도치 않게 재선언 하여 선언병합이 발생할 수 있다.)

2.1.2. 각각 언제 사용하나요?

type은 어떤 타입이든 선언 할 수 있지만 interface는 객체 타입만 선언 가능합니다. type은 단일타입, interface를 사용 할 수 없는 경우 (예 - react-navigation의 NavigatorParamList는 interface를 사용 할 수 없다.)를 만들때 사용된다. 그리고 나머지는 interface를 사용하는 편이다.

추가적으로 간단한 타입에서는 둘 사이에 크게 차이가 없지만 typescript 깃헙레포 위키문서의 PerformancePreferring Interfaces Over Intersections를 읽어보면 아래와 같은 글이 있습니다.

Much of the time, a simple type alias to an object type acts very similarly to an interface.

interface Foo { prop: string }

type Bar = { prop: string };

However, and as soon as you need to compose two or more types, you have the option of extending those types with an interface, or intersecting them in a type alias, and that's when the differences start to matter.

Interfaces create a single flat object type that detects property conflicts, which are usually important to resolve! Intersections on the other hand just recursively merge properties, and in some cases produce never. Interfaces also display consistently better, whereas type aliases to intersections can't be displayed in part of other intersections. Type relationships between interfaces are also cached, as opposed to intersection types as a whole. A final noteworthy difference is that when checking against a target intersection type, every constituent is checked before checking against the "effective"/"flattened" type.

For this reason, extending types with interfaces/extends is suggested over creating intersection types.

- type Foo = Bar & Baz & {
-     someProp: string;
- }
+ interface Foo extends Bar, Baz {
+     someProp: string;
+ }

간단한 타입은 typeinterface 둘다 비슷하게 동작하지만 두개이상의 타입을 유형을 확장하거나 교차(intersection) 할때 차이가 발생한다고 합니다. 완전히 이해가 되는 부분은 아니지만 여튼 여차저차해서 interface가 더 유리한 부분이 있다고 합니다. (영어 잘 하시는분계시면 설명좀..ㅠㅠ)

(performance 페이지는 typescript를 어느정도 공부하고 나서 한번 읽어볼만 한것 같습니다.)

2.2. 호출 시그니쳐(call signature)

내용보충 필요 참고링크

2.3. 인덱스 시그니쳐(index signature)

타입 속성의 모든 이름을 알 수는 없지만 값의 타입을 알 수 있는 경우일때 index signature를 사용하여 값의 유형을 설명 할 수 있습니다.

키가 string이고 값이 number인 타입을 선언하고 사용 할 수 있습니다.

interface Test {
  [x: string]: number;
}

const test: Test = {
  a: 0,
  b: 1,
  c: 2,
};

또는 Union Type을 통해 몇가지 값만 정할 수도 있습니다.

type Test = {
  [x in "a" | "b"]: number;
};

const test: Test = {
  a: 0,
  b: 1,
  c: 2, // Error: Object literal may only specify known properties, and 'c' does not exist in type 'Test'
};

object literal 형식을 사용하여 object의 키값을 타입의 키값으로 사용 할 수도 있습니다.

const testKeyMap = {
  a: 0,
  b: 1,
  c: 2,
} as const;

type Test = {
  [x in keyof typeof testKeyMap]: number;
};

const test: Test = {
  a: 0,
  b: 1,
  c: 2,
};

또한 값을 object literal 형식을 사용하여 object의 각 값을 타입으로 지정할 수 도 있습니다.

const testKeyMap = {
  a: 0,
  b: 1,
  c: 2,
} as const;

type Test = {
  [x in keyof typeof testKeyMap]: (typeof testKeyMap)[x];
};

/** 
 * 위 타입은 아래의 타입과 같다. 
 * 아래 타입과 다른점이라면 testKeyMap에 추가,삭제등의 수정이 있더라도 
 * 위 타입은 자동으로 추론되어 타입을 변경하지 않고 사용 할 수 있고 
 * 아래 타입은 testKeyMap이 수정 될때 마다 같이 수정되어야 한다. 
 * 
 * type Test = {
     a: 0,
     b: 1,
     c: 2,
 * }
*/

2.4. 중첩 인터페이스 (intersection type?)

인터섹션타입이라는 가정하에 정리

아래와 같이

interface Colorful {
  color: string;
}

interface Circle {
  radius: number;
}

type ColorfulCircle = Colorful & Circle;

const cc: ColorfulCircle = {
  color: "red",
  radius: 42,
};

2.5. 다중 인터페이스 확장

interface Colorful {
  color: string;
}

interface Circle {
  radius: number;
}

interface ColorfulCircle extends Colorful, Circle {}

const cc: ColorfulCircle = {
  color: "red",
  radius: 42,
};

2.6. 인터페이스 병합

위에서 설명한 선언병합과 같은내용

interface Box {
  height: number;
  width: number;
}
interface Box {
  scale: number;
}
let box: Box = { height: 5, width: 6, scale: 10 };

3. 함수

3.1. this와 화살표 함수

// todo

3.2. 나머지 매개변수

스프레드연산로 표기하고 매개변수 목록의 마지막에 위치함

function sum(first, seconds, ...numbers: number[]): number {
  return numbers.reduce((total, num) => total + num, 0) + first + seconds;
}

3.3. 콜백에서 this 매개변수

// todo

3.4. 오버로드

다양한 방식으로 호출 할 수 있는 함수를 정의

내용 보충 필요 참고링크

4. 유니언과 교차타입

4.1. 유니언타입과 교차타입의 사용사례

// 유니언타입
type SuccessResponse = {
  isSuccess: true;
  data: { id: number };
};

type FetchingResponse = {
  isFetching: true;
};

type QueryResponse = SuccessResponse | FetchingResponse;
// 교차타입
type ApiResponseBase = {
  code: number;
  message: string;
};

type ListApiResponse<T> = ApiResponseBase & {
  data: T[];
  pagination: {
    page: number;
    limit: number;
    hasNext: boolean;
    totalCount: number;
    totalPage: number;
  };
};

type DataApiResponse<T> = ApiResponseBase & {
  data: T;
};

4.2. 유니언 타입에서의 타입가드

// typeof를 사용한 타입가드
function typeGuardExample(a: number | string) {
  if (typeof a === "number") {
    return a.toLocaleString();
  }

  return a.toLowerCase();
}

// class와 instanceof를 사용한 타입가드
class ResponseError {
  code: number;
  message: string;
}

class JSSyntaxError {
  position: {
    line: number;
    col: number;
  };
  message: string;
}

function printError(error: ResponseError | JSSyntaxError) {
  if (error instanceof ResponseError) {
    console.log(error.code, error.message);
    return;
  }
  console.log(error.position.line, error.position.col, error.message);
}

// in 키워드를 사용한 타입 가드
type Car = {
  whill: number;
  door: number;
  window: number;
  hasSunRoof: boolean;
};

type Boat = {
  engine: string;
  speed: number;
  torque: number;
  size: "BIG" | "SMALL";
};

function printVehicle(vehicle: Car | Boat) {
  if ("whill" in vehicle) {
    console.log(
      `나는 자동차를 갖고있어! 바퀴는 ${vehicle.whill}개야 내차의 문 개수는 ${vehicle.window}`
    );
    return;
  }

  console.log(
    `나는 보트를 갖고 있어! 내 보트의 엔진은 ${vehicle.engine}이고 최대 토트는 ${vehicle.torque}토크야!`
  );
}

// 공통된 키값을 갖고 그 키값의 차이로 타입가드
// Discriminating Unions
type SuccessResponse = {
  isSuccess: true;
  data: { id: number };
};

type FetchingResponse = {
  isSuccess: false;
  isFetching: true;
};

type QueryResponse = SuccessResponse | FetchingResponse;

const test = (response: QueryResponse) => {
  console.log(response.data); // Error: Property 'data' does not exist on type 'QueryResponse'.
  // Property 'data' does not exist on type 'FetchingResponse'.
  if (response.isSuccess) {
    console.log(response.data);
  }
};

4.3. 교차타입의 장점과 사용법

// 두 타입을 합쳐서 사용할 수 있다.
// 예를 들들어 어드민 화면에서 정보 조회하는 API의 params는
// 검색필터와 데이터 목록의 페이지네이션 정보를 모두 필요로 하는데
// 검색 필터와 데이터의 페이지네이션 정보는 따로 관리를 해야 했고
// 아래와 같이 사용했다.

type TablePagination = {
  limit: number;
  page: number;
};

type SearchFilter = {
  id: number;
  name: string;
  phoneNumber: string;
  nickname: string;
};

type ApiResponseParams = TablePagination & Partial<SearchFilter>;

// 테이블과 검색필터를 합쳐 Api 요청 파라미터를 만들었고 타입중복을 줄이고 요구사항이 변경돼서 검색필터가 늘어나더라도 SearchFilter만 수정해주면 따로 ApiResponseParmas를 수정하지 않아도 된다.

5. 제너릭

5.1. 제너릭을 사용하는 이유(any와 비교)

// any로 되어있으면 사용하는 측에서 data의 타입을 활용 할 수 없음
type ApiResponse = {
  code: number;
  message: string;
  data: any;
};

// 제네릭으로 되어있을때는 화장이 용이하고 사용하는 측에서 data의 타입을 활용하여 추론 할 수 있다.
type ApiResponse<T> = {
  code: number;
  message: string;
  data: T;
};

5.2. 기본 제너릭 타입 정의(function, interface, type, class)

// interface
interface ApiResponse<T> {
  code: number;
  message: string;
  data: T;
}

// type
type ApiResponse<T> = {
  code: number;
  message: string;
  data: T;
};

// class
class DataList<T> {
  counter: T[];

  constructor(counter: T) {
    this.counter = [counter];
  }

  push(counter: T) {
    this.counter.push(counter);
  }
}

// function
const dataList = <T>(input: T) => {
  const list = [input];
  return {
    push: (input: T) => list.push(input),
  };
};

const list = dataList({ test: 1 });

list.push({ test: 2 });

list.push(1); // Error: Argument of type 'number' is not assignable to parameter of type '{ test: number; }'.ts(2345)

5.3. 제너릭 제약조건, 타입매개변수 사용

type GenericList<T extends Number> = T[];

const oneList: GenericList<1> = [1, 1, 1, 1, 1, 1];
const numberList: GenericList<Number> = [1, 2, 3, 4, 5, 6];
const stringList: GenericList<String> = []; // Error: Type 'String' does not satisfy the constraint 'Number'.
//  Type 'String' is missing the following properties from type 'Number': toFixed, toExponential, toPrecisionts(2344)

5.4. 제너릭 유틸리티 타입

5.4.1. Partial<T>

T의 모든 멤버를 옵셔널로 바꾼다.

type Toast = {
  source: boolean;
  vegetable: boolean;
  bread: boolean;
  option: Partial<Option>;
};

type Option = {
  ham: boolean;
  cheese: boolean;
  egg: boolean;
};

const makeToast = (ingredients: Toast) => {
  let toast = ["toast"];
  if (ingredients.bread && ingredients.source && ingredients.vegetable) {
    return "fail";
  }

  if (ingredients.option.ham) {
    toast = ["ham", ...toast];
  }
  if (ingredients.option.egg) {
    toast = ["egg", ...toast];
  }
  if (ingredients.option.cheese) {
    toast = ["cheese", ...toast];
  }

  return toast.join(" ");
};

5.4.2. - Readonly<T>

T의 모든 멤버를 읽기전용으로 변경합니다

let readonlyTest: Readonly<{ member: number }> = { member: 1 };

readonlyTest.member = 1; // Error: Cannot assign to 'member' because it is a read-only property.ts(2540)

5.4.3. - Record<K,T>

K의 타입을 Key로 갖고 T의 타입을 값으로 가지는 Object 타입을 만듭니다.

const recordTest: Record<string, number> = {
  zero: 0,
};

const recordTest2: Record<number, string> = {
  0: "zero",
};

5.4.4. - Pick<T,K>

T타입에서 K멤버만 가져옵니다.

const option: Pick<Toast, "option"> = { option: { cheese: true } };

5.4.5. - Omit<T,K>

T타입에서 K키 만 제외합니다.

const require: Omit<Toast, "option"> = {
  source: true,
  bread: true,
  vegetable: true,
};

5.4.6. - Exclude<T,U>

유니온 타입인 T에서 U를 제외한다.

// exclude = "bread" | "option" | "vegetable"
type ExcludeSource = Exclude<keyof Toast, "source">;
const exclude: ExcludeSource = "bread";

5.4.7. - Extract<T,U>

유니온 타입 T에서 U만 추출한다.

// exclude = "bread"
type ExcludeSource = Extract<keyof Toast, "source">;
const exclude: ExcludeSource = "source";

5.4.8. - NonNullable<T>

타입중 undefined 와 null을 제외한다.

// T0 = string
type T0 = NonNullable<string | number | undefined>;

5.4.9. - Parameters<T>

const testFn = (input1: number, input2: string) => input1 + input2;
type Params = Parameters<typeof testFn>;
const params: Params = [0, "hi"];

5.4.10. - ConstructorParameters<T>

추상클레스의 Parameter를 타입으로 정의합니다.

5.4.11. - ReturnType<T>

// Return = string
type Return = ReturnType<typeof testFn>;

5.4.12. - InstanceType<T>

// todo

5.4.13. - Required<T>

T의 멤버변수를 모두 필수값으로 합니다.

type ReachToast = {
  source: boolean;
  vegetable: boolean;
  bread: boolean;
  option: Required<Toast["option"]>;
};

5.4.14. - ThisParameterType<T>

// todo

5.4.15. - OmitThisParameter

// todo

5.4.16. - ThisType<T>

// todo

6.연산자

6.1. keyof, typeof 연산자

keyof는 object타입의 type이나 interface의 key를 추론할때 사용한다.

type Apple = {
  color: string;
  price: number;
};

// color | price
type AppleProps = keyof Apple;

typeof는 변수세상에 있는 변수를 타입의 세상으로 데리고 오는 역할을 합니다(좋은 설명이 생각나지 않아요..)
(벨류스페이스) (타입스페이스)

const test = "hi";

// "hi"
type TestType = typeof test;

const test1 = { key: "alt", id: 10 };

// { key: string, id: number }
type Test1Type = typeof test1;

// as const를 사용하면 구체적인 추론이 가능해진다.
// 이를 사용하여 enum을 대체할 수 있다.
const test1 = { key: "alt", id: 10 } as const;

/* 
{ 
   readonly key: "alt", 
   readonly id: 10 
}
*/
type Test1Type = typeof test1;

하지만 typeof는 변수에만 사용 할 수 있고 함수의 실행값에는 사용이 불가능하다.

const msgbox = (msg:string) => msg
// Error: ';' expected. msgbox뒤에 ; 를 넣어달라는 에러가 발생한다.
type MsgBoxReturnType =  typeof msgbox("Are you sure you want to continue?"

6.2. indexed type(인덱스 접근 유형)

type Person = { age: number; name: string; alive: boolean };
// number
type Age = Person["age"];
// number | string
type I1 = Person["age" | "name"];
// number | string | boolean
type I2 = Person[keyof Person];

// 이렇게도 사용가능
type AliveOrName = "alive" | "name";
type I3 = Person[AliveOrName];

indexed Access에 number를 사용하면 array의 각 멤버를 union으로 만들때 사용한다.

const numberArray = [1, 2, 3, 4, 5, 6, 7] as const;

// 1|2|3|4|5|6|7
type Indexed = (typeof numberArray)[number];

const MyArray = [
  { name: "Alice", age: 15 },
  { name: "Bob", age: 23 },
  { name: "Eve", age: 38 },
];

/*
{
    name: string,
    age: number,
}
*/
type Person = (typeof MyArray)[number];

6.3. 조건부 타입( + infer ) -> 강의 일부 참고

extends는 type세상의 삼항연산자입니다.

interface Animal {
  live(): void;
}
interface Dog extends Animal {
  woof(): void;
}

// Dog이 Animal의 확장이라면 Example1의 타입은 number이고 아니라면 string입니다.
// number
type Example1 = Dog extends Animal ? number : string;

// RegExp가 Animal의 확장이라면 Example2의 타입은 number이고 아니라면 string입니다.
// string
type Example2 = RegExp extends Animal ? number : string;

infer는 구체적인 타입을 추론합니다 주로 generic과 함께 사용됩니다. 제네릭을 사용한 util을 사용합니다.

// array를 입력받아 Array안의 값을 U로 유추하여 Array타입이 맞으면 유추된 U를 Array가 아니면 never를 return한다.
type Return<T> = T extends Array<infer U> ? U : never;

const f: Return<["test1" | "test2"]> = "test1";

Distributive Conditional Types(분산부 조건타입?)

// 이건 처음보는데 역시 문서를 자주 읽어야 한느것같다. any를 이렇게 이용한다니... extends 뒤쪽의 값을 사용하는것만 봤지 any를 사용하여 Type을 바로 사용 할수있을 줄은 몰랐다.
type ToArray<Type> = Type extends any ? Type[] : never;

// string[] | number[]
type StrArrOrNumArr = ToArray<string | number>;
// 대괄호([])로 Type과 any를 묶으면 다른 결과가나온다....와..
type ToArrayNonDist<Type> = [Type] extends [any] ? Type[] : never;

// 'ArrOfStrOrNum' is no longer a union.
// (string | number)[]
type ArrOfStrOrNum = ToArrayNonDist<string | number>;

6.4. mapped 타입 ( + 맵드 타입(Mapped Types)에서 속성을 제거하는 데 사용 하는 (-) 연산자 )

type OptionsFlags<Type> = {
  [Property in keyof Type]: boolean;
};
type Features = {
  darkMode: () => void;
  newUserProfile: () => void;
};

/*
{
  darkMode: boolean;
  newUserProfile: boolean;
}
*/
type FeatureOptions = OptionsFlags<Features>;

-연산자를 사용하여 readonly 또는 ?연산자를 제거 할 수 있습니다. +연산자를 사용하면 추가 할 수 있지만 쓰지 않아도 쓴것으로 간주한다고 합니다.

// readonly 제거하기
type CreateMutable<Type> = {
  -readonly [Property in keyof Type]: Type[Property];
};

type LockedAccount = {
  readonly id: string;
  readonly name: string;
};

type UnlockedAccount = CreateMutable<LockedAccount>;
// optional 제거하기
type Concrete<Type> = {
  [Property in keyof Type]-?: Type[Property];
};

type MaybeUser = {
  id: string;
  name?: string;
  age?: number;
};

type User = Concrete<MaybeUser>;
// typescript 4.1이상에서는 이런것도 가능하다!
type Getters<Type> = {
  // as 절이 없다면 Property를 추론하기 위한 절이지만 as 절을 사용하여 Property를 사용하여 새로운 타입을 만들어 낸다.
  [Property in keyof Type as `get${Capitalize<
    string & Property
  >}`]: () => Type[Property];
};

interface Person {
  name: string;
  age: number;
  location: string;
}

/*
{
    getName: () => string;
    getAge: () => number;
    getLocation: () => string;   
}
*/
type LazyPerson = Getters<Person>;

6.5. 템플릿 리터럴

javascript에서 사용하는 템플릿 리터럴을 type에서도 사용할 수 있습니다.

type World = "world";

type Greeting = `hello ${World}`;
type EmailLocaleIDs = "welcome_email" | "email_heading";
type FooterLocaleIDs = "footer_title" | "footer_sendoff";

// "welcome_email_id" | "email_heading_id" | "footer_title_id" | "footer_sendoff_id"
type AllLocaleIDs = `${EmailLocaleIDs | FooterLocaleIDs}_id`;

유틸!

type Greeting = "Hello, world";
// "HELLO, WORLD"
type ShoutyGreeting = Uppercase<Greeting>;
type Greeting = "Hello, world";
// "hello, world"
type QuietGreeting = Lowercase<Greeting>;
type LowercaseGreeting = "hello, world";
// "Hello, world"
type Greeting = Capitalize<LowercaseGreeting>;
type UppercaseGreeting = "HELLO WORLD";
// "hELLO WORLD"
type UncomfortableGreeting = Uncapitalize<UppercaseGreeting>;

6.7. 선택적 속성(?), readonly 연산자 - 1에서 함 (생략)

6.8. Non-null 단언 연산자(!)

7. 타입호환

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

유틸을 만들때 가장 중요한 부분 이 부분을 알면 유틸을 만들떄 막히지 않을 수 있다.

7.1.1 공변성(Covariance)

AB의 서브타입이면 T<A>T<B>의 서브타입이다.

typescript의 제네릭 타입들은 일반적으로 공변성을 가진다.

type A = { value1: string };
type B = { value2: number } & A;

type Generic<T> = { test: T };

type Covariance<T, U> = T extends U
  ? Generic<T> extends Generic<U>
    ? true
    : false
  : never;

type Test = Covariance<A, B>; // true
type Test = Covariance<B, A>; // false

`

7.1.2 반공변성(Contravariance)

AB의 서브타입이면, T<B>T<A>의 서브타입이다.

typescript에서 함수의 파라메터는 일반적으로 반공변성을 가진다.
(필요조건을 만족시키기만 하면된다!)

type A = { value1: string };
type B = { value2: number } & A;

type Generic<T> = (value: T) => void;

type Contravariance<T, U> = U extends T
  ? Generic<T> extends Generic<U>
    ? true
    : false
  : never;

type Test = Contravariance<A, B>; // true

함수의 매개변수는 반공변성 리턴타입은 공변성을 가진다.

type Contravariance<T, U> = T extends U ? true : false;

// true
type Test1 = Contravariance<
  (params: string | number) => number,
  (params: number) => number | boolean | string
>;
profile
React 개발자

0개의 댓글