[TS] TS for JS Programmer

chaevivi·2023년 2월 15일
0
post-thumbnail

TypeScript와 JavaScript의 관계는 다소 독특하다. TypeScript는 JavaScript의 모든 특징을 제공하면서, 그 위에 TypeScript의 타입 시스템이라는 층을 추가한다. 예를 들어, JavaScript는 이미 string, number, object, undefined 같은 원시 타입을 제공하고 있지만, 전체 코드베이스에 일관되게 할당되었는지 미리 확인해주지 않는다. 하지만 TypeScript는 확인해준다.

이는 기존에 동작하고 있는 JavaScript 코드는 또한 TypeScript 코드라는 것을 의미한다. TypeScript의 주요 이점은 코드에서 발생하는 예상치 못한 행동을 강조할 수 있고 이로 인해, 버그를 줄일 수 있다는 것이다.


타입 추론 (Types by Infenrence)

  • TypeScript는 JavaScript 언어를 알고 있으며 대부분의 경우 타입을 생성해 줄 것이다. 예를 들어 변수를 생성하면서 동시에 특정 값에 할당하는 경우, TypeScript는 그 값을 해당 변수의 타입으로 사용할 것이다.
    let helloWorld = "Hello world";
    // let helloWorld: string
    JavaScript가 동작하는 방식을 이해함으로써, TypeScript는 JavaScript 코드를 받아들이면서 타입 시스템을 구축할 수 있다. 이는 코드에서 타입을 명시하기 위해 추가로 문자를 사용할 필요 없는 타입 시스템을 제공한다. 이것이 위의 예제에서 TypeScript가 helloWorldstring임을 알게 되는 방식이다.
    JavaScript를 VSCode에서 작성할 때 편집기의 자동 완성 기능을 사용했을 것이다. 이는 VSCdoe가 Javascript의 작업을 개선하기 위해 TypeScript를 내부적으로 사용했기 때문이다.

타입 정의하기 (Defining Types)

  • JavaScript는 다양한 디자인 패턴을 가능하게 하는 동적 언어이다. 하지만 몇몇 디자인 패턴은 자동으로 타입을 제공하기 힘들 수 있는데 (예를 들어, 동적 프로그래밍의 사용) 이러한 경우에 TypeScript는 TypeScript 에게 어떤 타입이 되어야 하는지 명시 가능한 JavaScript 언어의 확장을 지원한다. 다음은 name: stringid: number 을 포함하는 추론 타입을 가진 객체를 생성하는 예제이다.
    const user = {
    	name: "daeman",
    	id: 14,
    };
    이 객체의 형태를 명시적으로 나타내기 위해서는 interface로 선언한다,
    interface User {
    	name: string;
    	id: number;
    }
    이제 변수 선언 뒤에 : TypeName 의 구문을 사용해서 JavaScript 객체가 새로운 interface 의 형태를 따르고 있음을 선언할 수 있다.
    interface User {
    	name: string;
    	id: number;
    }
    // --- cut ---
    const user: User = {
    	name: "daeman",
    	id: 14,
    };
    해당 인터페이스에 맞지 않는 객체를 생성하면 TypeScript는 경고를 준다.
    // @errors: 2322
    interface User {
    	name: string;
    	id: number;
    }
    // --- cut ---
    const user: User = {
    	username: "daeman",  // X
    	id: 14,
    };
    JavaScript는 클래스와 객체 지향 프로그래밍을 지원하기 때문에, TypeScript 또한 동일하다. - 인터페이스는 클래스로도 선언할 수 있다.
    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("daeman", 14);
    인터페이스는 함수에서 매개변수와 리턴값을 명시하는데 사용되기도 한다.
    // @noErrors
    interface User {
    	name: string;
    	id: number;
    }
    
    // --- cut ---
    function getAdminUser(): User {
    	// ...
    }
    
    function deleteUser(user: User) {
    	// ...
    }
  • JavaScript에서 사용할 수 있는 적은 종류의 원시 타입이 이미 있다. : boolean, bigint, null, number, string, symbol, object, undefined는 인터페이스에서 사용할 수 있다. TypeScript에서는 여기에 몇 가지를 추가했다. any (무엇이든 허용), unknown (이 타입을 사용하는 사람이 어떤 타입으로 정의했는지 확인), never (이 타입은 발생할 수 없다), void (undefined를 리턴하거나 리턴할 값이 없는 함수) 타입 구축을 위한 두 개의 구문이 있다 : Interfaces와 Types. interface를 우선적으로 사용하고 types는 특정한 기능이 필요할 때만 사용한다.

타입 구성 (Composing Types)

  • TypeScript에서는 간단한 타입들을 결합해서 복잡한 타입들을 만들어낼 수 있다. 여기에는 대표적으로 두 가지 방법이 있다 : unions를 사용하는 것과 generics를 사용하는 것.

Unions

  • 유니온을 사용하면, 여러 타입 중 하나가 될 수 있다고 선언할 수 있다. 예를 들어, true나 false인 boolean 타입을 선언할 수 있다.
    type MyBool = true | false;
    Note : MyBool은 boolean 타입이며, 구조적 타입 시스템의 프로퍼티이다. 유니온 타입이 많이 사용되는 사례는 값이 될 수 있는 string이나 number의 리터럴 집합을 묘사할 때이다.
    type WindowStates = "open" | "closed" | "minimized";
    type LockStates = "locked" | "unlocked";
    type PositiveOddNumbersUnderTen = 1 | 3 | 5 | 7 | 9;
    유니온은 다른 타입들을 다루는 방법도 제공한다. 예를 들어, array나 string 타입을 갖는 함수를 생성할 수 있다
    function getLength(obj: string | string[]) {
    	return obj.length;
    }
  • 값의 타입을 알고싶다면, typeof를 사용하면 된다.
    TypePredicate
    stringtypeof s === “string”
    numbertypeof n === “number
    booleantypeof b === “boolean”
    undefinedtypeof undefined === “undefined”
    functiontypeof f === “function”
    arrayArray.isArray(a)
    예를 들어, string이나 array로 결정되는 다른 값들을 리턴하는 함수를 만들 수 있다.
    function wrapInArray (obj: string | string[]) {
    	if (typeof obj === "string") {
    		return [obj];  // (parameter) obj: string
    	}
    	return obj;  // (parameter) obj: string[]
    }

Generics

  • 제네릭은 타입에 변수를 제공한다. 일반적인 예제는 array이다. 제네릭을 사용하지 않은 array는 어떤 것이든 담을 수 있다. 하지만 제네릭을 사용한 array는 array가 담은 값들의 타입을 나타낼 수 있다.
    type StringArray = Array<string>;
    type NumberArray = Array<number>;
    type ObjectWtihNameArray = Array<{ name: string }>;
    제네릭을 사용하면 고유의 타입을 선언할 수 있다.
    interface Backpack<Type> {
    	add: (obj: Type) => void;
    	get: () => Type;
    }
    
    // 이 줄은 TypeScript에 `backpack`이라는 상수가 있다고 알리는 쉬운 방법이며,
    // 어디서 왔는지 걱정하지 않아도 된다.
    declare const backpack: BackPack<string>;
    
    // object는 string이다, 왜냐하면 위에서 BackPack을 변수 부분에서 선언했기 때문이다.
    const object = backpack.get();
    
    // backpack 변수는 string이기 때문에, add 함수에 숫자를 넘겨줄 수 없다.
    backpack.add(23);  // error
    // number 타입의 인수는 string 타입의 파라미터에 할당 할 수 없다.

구조적 타입 시스템 (Structural Type System)

  • TypeScript의 핵심 원칙 중 하나는 값이 가지고 있는 형태에 집중하여 타입을 체크하는 것이다. 이것은 가끔 “duck typing”이나 “structural typing”이라고 불린다. 만약 두개의 객체가 똑같은 형태를 가지고 있으면, 구조적 타입 시스템에서는 똑같은 타입으로 여겨진다.
    interface Point {
    	x: number;
    	y: number;
    }
    
    function logPoint(p: Point) {
    	console.log(`${p.x}, ${p.y}`);  // 12, 26
    }
    
    const point = { x: 12, y: 26 };
    logPoint(point);
    point 변수는 Point 타입으로 선언되지 않았다. 하지만, TypeScript는 타입 체크를 할 때, point의 형태를 Point의 형태와 비교했다. 둘 다 똑같은 형태를 가졌고, 그래서 코드가 통과했다. 이러한 형태 매칭은 일치하는 객체 필드의 하위 집합만 필요하다.
    const point3 = { x: 12, y: 26, z: 89 };
    logPoint(point3);  // 12, 26
    
    const rect = { x: 33, y: 3, width: 30, height: 80 };
    logPoint(rect);  // 33, 3
    
    const color = { hex: "#187ABF" };  // error
    // '{ hex: "#187ABF" }' 인자는 'Point' 타입의 파라미터에 할당할 수 없다.
    // '{ hex: "#187ABF" }' 타입은 '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);  // 13, 56
    만약 객체나 클래스에 필요한 모든 속성이 있는 경우, TypeScript는 구현 세부 사항에 관계 없이 일치한다고 말할 것이다.


출처
(en) https://www.typescriptlang.org/docs/handbook/typescript-in-5-minutes.html
(kor) https://typescript-kr.github.io/pages/tutorials/ts-for-js-programmers.html

profile
직접 만드는 게 좋은 프론트엔드 개발자

0개의 댓글