프론트엔드/Typescript

[Typescript] 타입스크립트 타입 종류

순코딩 2024. 8. 16. 10:58

https://www.typescriptlang.org/ko/docs/handbook/2/everyday-types.html#%EC%9B%90%EC%8B%9C-%ED%83%80%EC%9E%85--string-number-%EA%B7%B8%EB%A6%AC%EA%B3%A0-boolean

 

Documentation - Everyday Types

언어의 원시 타입들.

www.typescriptlang.org

 

원시 타입
  • string
  • number
  • boolean

 

배열

ex) [1,2,3]

=> number[]

 

모든 타입

any

 

사용법
변수에 대한 타입 표기
let myName: string = "Alice";

하지만 대부분의 경우, 타입 표기는 필요하지 않습니다.

가능하다면 TypeScript는 자동으로 코드 내의 있는 타입들을 추론하고자 시도합니다.

예를 들어, 변수의 타입은 해당 변수의 초깃값의 타입을 바탕으로 추론됩니다.

 

함수에 대한 타입 표기

매개변수 타입 표기

// 매개변수 타입 표기
function greet(name: string) {
  console.log("Hello, " + name.toUpperCase() + "!!");
}

매개변수에 타입이 표기되었다면, 해당 함수에 대한 인자는 검사가 이루어집니다.

(매개변수에 타입을 표기하지 않았더라도, 여전히 TypeScript는 올바른 개수의 인자가 전달되었는지 여부를 검사합니다.)

// 만약 실행되면 런타임 오류가 발생하게 됩니다!
greet(42);
Argument of type 'number' is not assignable to parameter of type 'string'.

 

반환 타입 표기

function getFavoriteNumber(): number {
  return 26;
}

변수의 타입 표기와 마찬가지로, 반환 타입은 표기하지 않아도 되는 것이 일반적입니다.

왜냐하면 TypeScript가 해당 함수에 들어있는 return 문을 바탕으로 반환 타입을 추론할 것이기 때문입니다.

위 예시에서 사용된 타입 표기는 큰 의미를 갖지 않습니다.

때에 따라 문서화를 목적으로, 또는 코드의 잘못된 수정을 미연에 방지하고자, 혹은 지극히 개인적인 선호에 의하여 명시적인 타입 표기를 수행하는 코드도 존재합니다.

 

익명 함수

익명 함수는 함수 선언과는 조금 다릅니다.

함수가 코드상에서 위치한 곳을 보고 해당 함수가 어떻게 호출될지 알아낼 수 있다면, TypeScript는 해당 함수의 매개 변수에 자동으로 타입을 부여합니다.

// 아래 코드에는 타입 표기가 전혀 없지만, TypeScript는 버그를 감지할 수 있습니다.
const names = ["Alice", "Bob", "Eve"];
 
// 함수에 대한 문맥적 타입 부여
names.forEach(function (s) {
  console.log(s.toUppercase());
Property 'toUppercase' does not exist on type 'string'. Did you mean 'toUpperCase'?
});
 
// 화살표 함수에도 문맥적 타입 부여는 적용됩니다
names.forEach((s) => {
  console.log(s.toUppercase());
Property 'toUppercase' does not exist on type 'string'. Did you mean 'toUpperCase'?
});

매개 변수 s에는 타입이 표기되지 않았음에도 불구하고, TypeScript는 s의 타입을 알아내기 위하여 배열의 추론된 타입과 더불어 forEach 함수의 타입을 활용하였습니다.

이 과정은 문맥적 타입 부여라고 불리는데, 왜냐하면 함수가 실행되는 문맥을 통하여 해당 함수가 가져야 하는 타입을 알 수 있기 때문입니다.

 

객체 타입
// 매개 변수의 타입은 객체로 표기되고 있습니다.
function printCoord(pt: { x: number; y: number }) {
  console.log("The coordinate's x value is " + pt.x);
  console.log("The coordinate's y value is " + pt.y);
}
printCoord({ x: 3, y: 7 });

위에서 매개변수는 x와 y라는 두 개의 프로퍼티로 이루어진 타입으로 표기되고 있는데, 두 값은 모두 number 타입입니다.

각 프로퍼티를 구분할 때 , 또는 ;를 사용할 수 있고, 가장 마지막에 위치한 구분자의 표기는 선택 사항입니다.

각 프로퍼티의 타입 표기 또한 선택 사항입니다. 

만약 타입을 지정하지 않는다면, 해당 프로퍼티는 any 타입으로 간주합니다.

 

옵셔널 프로퍼티

객체 타입은 일부 또는 모든 프로퍼티의 타입을 선택적인 타입, 즉 옵셔널로 지정할 수 있습니다.

프로퍼티 이름 뒤에 ?를 붙이면 됩니다.

function printName(obj: { first: string; last?: string }) {
  // ...
}
// 둘 다 OK
printName({ first: "Bob" });
printName({ first: "Alice", last: "Alisson" });

JavaScript에서는 존재하지 않는 프로퍼티에 접근하였을 때, 런타임 오류가 발생하지 않고 undefined 값을 얻게 됩니다.

이 때문에 옵셔널 프로퍼티를 읽었을 때, 해당 값을 사용하기에 앞서 undefined인지 여부를 확인해야 합니다.

function printName(obj: { first: string; last?: string }) {
  // 오류 - `obj.last`의 값이 제공되지 않는다면 프로그램이 멈추게 됩니다!
  console.log(obj.last.toUpperCase());
'obj.last' is possibly 'undefined'.
  if (obj.last !== undefined) {
    // OK
    console.log(obj.last.toUpperCase());
  }
 
  // 최신 JavaScript 문법을 사용하였을 때 또 다른 안전한 코드
  console.log(obj.last?.toUpperCase());
}

 

유니언 타입

TypeScript의 타입 시스템에서는 기존의 타입을 기반으로 다양한 연산자를 사용하여 새로운 타입을 만들 수 있습니다.

function printId(id: number | string) {
  console.log("Your ID is: " + id);
}
// OK
printId(101);
// OK
printId("202");
// 오류
printId({ myID: 22342 });
Argument of type '{ myID: number; }' is not assignable to parameter of type 'string | number'.

유니언 타입에 맞는 값을 제공하는 것은 간단합니다.

유니언 타입의 멤버 중 하나에 해당하는 타입을 제공하면 됩니다. 

 

TypeScript에서 유니언을 다룰 때는 해당 유니언 타입의 모든 멤버에 대하여 유효한 작업일 때에만 허용됩니다.

예를 들어 string | number라는 유니언 타입의 경우, 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'.
}

이를 해결하려면 코드상에서 유니언을 좁혀야 하는데, 이는 타입 표기가 없는 JavaScript에서 벌어지는 일과 동일합니다.

function printId(id: number | string) {
  if (typeof id === "string") {
    // 이 분기에서 id는 'string' 타입을 가집니다
 
    console.log(id.toUpperCase());
  } else {
    // 여기에서 id는 'number' 타입을 가집니다
    console.log(id);
  }
}

 

타입 별칭(Type Aliases)

우리는 객체 유형과 유니온 유형을 유형 주석에 직접 작성하여 사용해 왔습니다.

이는 편리하지만, 동일한 유형을 두 번 이상 사용하고 단일 이름으로 참조하려는 경우가 많습니다.

type Point = {
  x: number;
  y: number;
};
 
// Exactly the same as the earlier example
function printCoord(pt: Point) {
  console.log("The coordinate's x value is " + pt.x);
  console.log("The coordinate's y value is " + pt.y);
}
 
printCoord({ x: 100, y: 100 });

 

인터페이스(Interfaces)

인터페이스 선언은 객체 유형의 이름을 지정하는 또 다른 방법입니다.

interface Point {
  x: number;
  y: number;
}
 
function printCoord(pt: Point) {
  console.log("The coordinate's x value is " + pt.x);
  console.log("The coordinate's y value is " + pt.y);
}
 
printCoord({ x: 100, y: 100 });

 

타입 별칭과 인터페이스의 차이점

https://www.typescriptlang.org/docs/handbook/2/everyday-types.html

 

Documentation - Everyday Types

The language primitives.

www.typescriptlang.org

 

타입 어설션(Type Assertions)

예를 들어 document.getElementById를 사용하는 경우 TypeScript는 이것이 어떤 종류의 HTMLElement를 반환한다는 것만 알고 있지만 페이지에 항상 지정된 ID를 가진 HTMLCanvasElement가 있다는 것을 알 수 있습니다.

const myCanvas = document.getElementById("main_canvas") as HTMLCanvasElement;

유형 어설션과 마찬가지로 유형 어설션은 컴파일러에 의해 제거되며 코드의 런타임 동작에 영향을 주지 않습니다.

이와 동등한 대괄호 구문을 사용할 수도 있습니다(코드가 .tsx 파일에 있는 경우 제외):

const myCanvas = <HTMLCanvasElement>document.getElementById("main_canvas");