타입스크립트는 자바스크립트와 특이한 관계를 가지고 있다. 타입스크립트는 자바스크립트의 모든 기능을 제공하고, 그 위에 추가적인계층을 제공하는데: 타입스크립트의 타입 시스템이다.
예를들면, 자바스크립트는 string과 number같은 언어의 primitives들을 제공하는데, 타입이 잘 배정되었는지 끊임없이 체크하지는 않는다. 타입스크립트는 끊임없이 체크한다.
이건 이미 자바스크립트로 작성된 코드가 타입스크립트로도 동작한다는 걸 의미한다. 타입스크립트의 주요 이점은 코드에서 기대하지 않은 동작을 하이라이트 해줘서 버그의 빈도를 낮춘다는 것이다.
타입 추론
타입스크립트는 자바스크립트 언어를 알고, 많은 상황에서 타입을 생성할 것이다. 예를들면, 변수를 생성하고 특정한 값을 할당하면, 타입스크립트는 그 타입으로 값을 사용한다.
let helloWorld = "Hello World";
자바스크립트가 어떻게 동작하는지 이해해서, 타입스크립트는 타입을 가지고 자바스크립트 코드에 접근하는 타입시스템을 만들 수 있다. 이건 코드에서 명시적으로 타입을 만드는 추가적인 캐릭터를 추가할 필요없는 타입시스템이다. 이게 위 예제에서 타입스크립트가 hellowWorld가 string이라는 것을 아는 이유이다.
비쥬얼 스튜디오에서 자바스크립트를 쓰고, 에디터의 자동완성을 썻을 거다. 비쥬얼 스튜디오 코드는 자바스크립트를 보다 쉽게 작성하기 위해 내부적으로 타입스크립트를 사용했다.
타입 정의하기
자바스크립트에서 디자인 패턴의 넓은 다양성을 사용할 수 있다. 몇몇 디자인 패턴은 타입들이 자동으로 추론되기 어렵게 만들었다. (예를 들면, 다이나믹 프로그래밍을 사용하는 패턴들). 이 경우를 커버하기 위해, 타입스크립트는 자바스크립트 언어의 확장을 지원한다. 타입스크립트에게 어떤 타입이 가능한지 여지를 제공하는 확장이다.
예를들면, name: string과 id: number를 포함하는 추론 타입을 가진 객체를 만들 기 위해, 이렇게 작성할 수있다 :
const user = {
name: "Hayes",
id: 0,
};
인터페이스 선언을 사용한 객체의 모양으로 명시적으로 묘사할 수 있다:
interface User {
name: string;
id: number;
}
자바스크립트 객체를 변수 선언이후 : TypeName같은 문법을 사용해서 위 인터페이스의 모양을 따르도록 선언할 수 있다.
const user: User= {
name: "Hayes",
id: 0,
};
당신이 제공한 인터페이스와 매칭되지 않는 객체를 작성했다면, 타입스크립트는 경고할 것이다:
interface User {
name: string;
id: number;
}
const user: User = {
username: "Hayes",
// warn : 'username' does not exist in type 'User'.
id: 0,
};
자바스크립트가 객체 지향 프로그래밍과 클래스를 지원하기 때문에, 타입스크립트도 마찬가지다. 인터페이스 선언을 클래스를 써서 할 수 있다.
interface User {
name: string;
id: number;
}
class UserAccount {
name: string;
id: number;
constructor(name: string, id: number) {
this.name = name;
this.id = id;
}
}
const user: User = new UserAccount("Murphy", 1);
인터페이스를 파라미터의 주석과 함수의 리턴값에도 사용할 수 있다:
function getAdminUser(): User {
// ...
}
function deleteUser(user: User) {
// ...
}
자바스크립트에서 가능한 primitive types의 작은 묶음이 있다: boolean, bigint, null, number, string, symbol 그리고 uindefuned, 이 묶음은 인터페이스에서 사용가능하다. 타입스크립트는 이 것들을 좀 더 연장했다. any (어떤것이든 허용), unknwon (타입이 무엇인지 이 타입을 사용하는 누군가가 선언하도록 함), never(이 타입은 가능하지 않음), 그리고 void(undefined나 리턴값을 리턴하지 않는 함수) 이다.
Interfaces and Ypes 에서 빌딩 타입을 위한 두개의 문법을 볼 수 있다. 인터페이스를 선호해야 한다. 특정 기능이 필요할 때 type을 써라.
타입 구성
타입스크립트에서, 간단한 타입들을 혼합해서 복잡한 타입을 만들 수 있다. 두가지 보편적 방법이 있는데: unions을 사용하는 방법, generic을 사용하는 방법이다.
Unions
유니온을 사용하면, 많은 타입중 하나로 타입을 선언할 수 있다. 예를 들면, boolean타입을 true 또는 false 중 하나로 묘사할 수 있다.
type MyBool = true | false;
주의: MyBool 위에 마우스를 겹치면(hover), MyBool의 클래스가 boolean인걸 볼 수 있을거다. 구조형 타입 시스템의 프로퍼티 이다. 자세한 내용은 아래를 참고하라.
유니온 타입의 흔한 사용법은 값으로 허용되는 string의 모음이나 숫자 리터럴이다.
type WindowStates = "open" | "closed" | "minimized";
type LockStates = "locked" | "unlocked";
type PositiveOddNumbersUnbderTen = 1 | 3 | 5 | 7 | 9;
우니온은 다른 타입을 다루는 방법도 제공한다. 예를들면, array나 string을 받는 함수도 만들 수 있다 :
function getLength(obj: string | string[]) {
return obj.length;
}
변수의 타입을 알고 싶으면, typeof를 사용하라:
Type | Predicate |
string | typeof s === "string" |
number | typeof n === "number" |
boolean | typeof b === "boolean" |
undefined | typeof undefined === "undefined" |
function | typeof f === "function" |
array | Array.isArray(a) |
예를 들면, string이나 array중 어떤걸 넘기는지에 따라 다른 값을 리턴하는 함수를 만들 수 있다:
function wrapInArray(obj: string | string[]) {
if (typeof obj === "string") {
return [obj]; // (paramete) obj: string
}
return obj;
}
Generics
제네릭은 변수들을 타입으로 제공한다. 흔한 예는 array이다. 제네릭이 없는 배열은 무엇이든 담을 수 있다. 제네릭을 사용한 배열은 배열이 담을 변수를 설명할 수 있다.
type StringArray = Array<string>;
type NumberArray = Array<number>;
type ObjectWithNameArray = Array<{ name: string }>;
제네릭을 사용한 타입을 선언할 수 있다.
interface Backpack<Type> {
add: (obj: Type) => void;
get: () => Type;
}
// 이 선언부는 타입스크립트를 설명하기 위한 줄로,
// 호출된 'backpack'상수에 대해 걱정하지 마라.
declare const backpack: Backpack<string>;
// 객체는 string이다. 왜냐면 위에서 Backpack의 변수 부분으로 선언했기 떄문이다.
const object = backpack.get();
// backpack 변수가 string이어서, add 함수에 number를 보낼 수 없다.
backpack.add(23);
/* warn: Argument of type 'number' is not assignable to parameter of type 'string'
구조적인 타입 시스템
타입스크립트의 핵심 원칙 중 하나는 타입 체킹은 변수가 가진 모양에 초점을 맞춘다는 것이다. 이건 "duck typing"이나 "structural typing"이라고 부른다.
구조적인 타입 시스템에서, 같은 모양의 두개의 객체가 있고, 그 객체들이 같은 타입을 다룬다:
interface Point {
x: number;
y: number;
}
function logPoint(p: Point) {
console.log(`${p.x}, ${p.y}`);
}
// logs "12, 26"
const point = { x: 12, y: 26 };
logPoint(point);
point 변수는 Point타입으로 선언되지는 않다. 하지만, 타입스크립트는 타입체크에서 point의 모양과 Point의 모양을 비교한다. 같은 모양을 가졌다면, 코드가 통과한다.
shape-matching은 매칭하기위해 객체 필드 중 부분집합만 필요로 한다.
const point3 = { x: 12, y: 26, z: 89 };
logPoint(point3); // logs "12, 26"
const rect = { x: 33, y: 3, width: 30, height: 80 };
logPoint(rect); // logs "33, 3"
const color = { hex: "#187ABF" };
logPoint(color);
// Argument of type '{ hex: string; }' is not assignable to parameter of type 'Point'.
// Type '{ hex: string; }' is missing the following properties from type 'Point': x, y
클래스와 객체가 모양을 따르는 방법에는 차이가 없다.
class VirtualPoint {
x: number;
y: number;
constructor(x: number, y: number) {
this.x = x;
this.y = y;
}
}
const newVPoint = new VirtualPoint(13, 56);
logPoint(newVPoint); // logs "13, 56"
만약 객체가 클래스가 필요로하는 프로퍼티를 모두 가졌다면, 구현사엣와 상관없이, 타입스크립트는 매칭된다고 말할것이다.
'front > ts' 카테고리의 다른 글
[The Basics] Explicit Types (0) | 2022.05.16 |
---|---|
[The Basics] Emitting with Errors (0) | 2022.05.16 |
[The Basics] tsc, the TypeScript compiler (0) | 2022.05.13 |
[The Basics] Non-exception Failures (0) | 2022.05.13 |
The TypeScript Handbook (0) | 2022.05.11 |
댓글