📌 타입스크립트란?

JavaScript는 동적 타입만을 제공하여 예측하기 어려운 타입변환으로 디버깅이 어려워지는 문제점이 있어 이를 해결하고자 TypeScript가 탄생하였다.

TypeScript는 정적 타입 시스템을 사용하여 코드가 실행되기 전에 코드에 대하여 예측해준다.

JavaScript 위의 레이어로 자리잡고 있어 JavaScript 기능을 제공하면서 그 위에 타입 시스템이라는 레이어를 추가한 것이다.

즉, TypeScript는 JavaScript 기능과 타입 시스템 기능을 제공하는 프로그래밍 언어입니다.

🤿 타입 추론

1
let helloWorld = "Hello World";

타입추론

위와 같이 JavaScript 코드를 작성하면 TypeScript는 위 사진같이 타입을 추론합니다.

✏️ 타입 정의

객체 타입 정의

타입을 구축하기 위해 우선적으로 interface 구문을 사용하고 특정 기능이 필요할 때, type을 사용한다.

1
2
3
4
interface User {
name: string;
id: number;
}

이제 변수 뒤에 : TypeName 구문을 사용하여 JavaScript 객체가 interface 형태를 따르고 있다는 것을 선언할 수 있다.

1
2
3
4
const user: User = {
name: "Hayes",
id: 0,
}; // 성공

하지만 해당 interface 형태와 다른 객체에 선언하면 오류를 발생시킨다.

1
2
3
4
5
6
7
8
9
// @errors: 2322
interface User {
name: string;
id: number;
}
const user: User = {
username: "Hayes",
id: 0,
};

인터페이스는 클래스로도 선언이 가능하다.

1
2
3
4
5
6
7
8
9
10
11
12
13
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);

인터페이스는 함수에서 매개변수와 리턴 값을 명시할 수 있다.

1
2
3
4
5
6
7
8
9
10
11
12
// @noErrors
interface User {
name: string;
id: number;
}
// ---cut---
function getAdminUser(): User {
//...
}
function deleteUser(user: User) {
// ...
}

🐥 타입 구성

interface 구문 대신 여러가지 타입을 이용하여 새 타입을 작성하기 위한 용도로 유니온(Unions)과 제네릭(Generic) 이 있다.

유니온(Unions)

1
type testBool = true | false;

유니온 타입이 가장 많이 사용되는 사례는 허용되는 타입들을 집합으로 나타낼 때이다.

1
2
3
4
5
type WindowStates = "open" | "closed" | "minimized";
type OddNumbersUnderTen = 1 | 3 | 5 | 7 | 9;

const a: WindowStates = "open";
const arr: OddNumbersUnderTen = [1, 2, 3, "4"]; // error: Type 'string' is not assignable to type 'number'.

제네릭(generic)

제네릭은 타입에 변수를 제공하는 방법이다.

1
2
3
4
5
6
7
8
type StringArray = Array<string | number>;
type ObjectWithNameArray = Array<{ name: string }>;

const strArr: StringArray = ["str", "apple", 3]; // 성공

const objWithArr: ObjectWithNameArray = [{ name: "yiju" }, { age: 18 }]; // Error
// Type '{ age: number; }' is not assignable to type '{ name: string; }'.
// Object literal may only specify known properties, and 'age' does not exist in type '{ name: string; }'.

제네릭으로 변수에 타입을 할당하고 이를 다른 타입에게 전달하거나 할 수 있다.

1
2
3
function identity<Type>(arg: Type): Type {
return arg;
}
  • 만약 arg에 number가 들어오게 되면 Type 변수가 이를 기억했다가 identity 함수의 반환값도 number로 할당해준다.
1
2
3
4
5
6
7
8
9
function loggingIdentity<Type>(arg: Type[]): Type[] {
console.log(arg.length); // 배열은 .length를 가지고 있습니다. 따라서 오류는 없습니다.
return arg;
}

function loggingIdentity<Type>(arg: Array<Type>): Array<Type> {
console.log(arg.length); // 배열은 .length를 가지고 있습니다. 따라서 오류는 없습니다.
return arg;
}
  • 만약 배열을 받아서 그 길이를 출력하고 배열을 반환하고 싶다면 위와 같이 작성할 수 있다.

⛳️ 구조적 타입 시스템

TypeScript 핵심 원칙 중 하나는 타입 검사가 값이 있는 형태에 초점을 맞춘다는 것이다. 이는 duck typing 또는 구조적 타이핑이라고 부른다.

구조적 타입 시스템에서 두 객체가 같은 형태를 가지면 같은 것으로 간주한다.

1
2
3
4
5
6
7
8
9
10
interface 오리 {
꽥꽥: () => void;
}

function 오리인척하기(duck: 오리) {
duck.꽥꽥();
}

const 고양이 = { 꽥꽥: () => console.log("꽥꽥") };
오리인척하기(고양이); // 꽥꽥
  • 고양이 변수는 오리라는 타입으로 선언된 적이 없지만, 내부에 꽥꽥이라는 메서드를 가지고 있으므로 고양이 객체를 오리인척하기 함수에 인수로 전달하여 호출하였더닌 고양이가 꽥꽥을 출력하였다.
1
2
3
4
5
6
7
8
9
10
11
12
interface Point {
x: number;
y: number;
}

function printPoint(p: Point) {
console.log(`${p.x}, ${p.y}`);
}

// "12, 26"를 출력합니다
const point = { x: 12, y: 26 };
printPoint(point);
  • point 변수에 Point type이 선언된 적이 없지만 같은 형태를 가지고 있어 정상적으로 동작하는 것을 볼 수 있다.