Typescript 문법 정리 1편

김승재·2022년 11월 24일
0

Vue

목록 보기
2/3
post-thumbnail

👋 0. Typescript 소개

💡 타입스크립트란 ?
타입스크립트 (Typescript)는 Microsoft에서 개발하고 유지/관리하는 Apache 라이센스가 부여된 오픈 소스입니다. 일반 자바스크립트로 컴파일 되는 자바 스크립트 Superset(상위 호환)으로 2012년 10월 처음 릴리스 되었습니다.
자바스크립트는 타입 시스템이 없는 동적 프로그래밍 언어로, 자바스크립트 변수는 문자열, 숫자, 불린 등 여러 타입의 값을 가질 수 있습니다. 이를 약한 타입 언어라고 표현할 수 있으며 비교적 유연하게 개발할 수 있는 환경을 제공하는 한편 런타임 환경에서 쉽게 에러가 발생할 수 있는 단점을 가집니다.
그렇기 때문에, 런타임이 아닌 컴파일 환경에서 에러가 발생하는 오류를 사전에 잡아낼 수 있게, 타입스크립트는 Java와 같이 강한 타입 시스템을 제공하며, 체계적이고 높은 코드 품질을 제공할 수 있습니다.

Vue2에서 Vue3으로 버젼이 업데이트 되면서 가장 눈에 들어온 변환점은 바로 타입스크립트와의 호환성이 좋아진 점입니다. 기존 Vue2에서 타입스크립트를 사용하기 위해서는 큰 비용 및 개발 공수가 필요하였는데, 이 점이 개선되어 이번 토이프로젝트에서 사용해보기로 정하게 되었는데요. 저는 타입스크립트를 처음 사용하여 일단 기본적인 문법부터 정리해보기로 하였습니다.

저는 연습과정에서 공식 문서도 읽어보고 하였지만, 직접 써보는게 아무래도 더 이해하기 편할거같아서 아래 두 사이트를 이용하였습니다.

  • 타입스크립트 연습 사이트 : 타입스크립트 연습문제가 16문제 주어지는 사이트입니다. 문제도 좋고 답도 편하게 열람할 수 있어서 막힘없이 진행할 수 있어서 좋았습니다. ㅎ 독자님들도 타입스크립트가 처음이라면 추천합니다 👍

  • 타입스크립트 Playground : 온라인 환경에서 바로 타입스크립트를 연습할 수 있어서 많이 편합니다.


1. Basic Typings

타입스크립트는 변수선언시 다음과 같이 다양한 타입을 선언할 수 있습니다.

let isBoolean: boolean; // 참/거짓 값

let num: number; // 숫자 (정수 + 소수 + Infinity + NaN)

let str: string; // 문자열

let alphanumber : string | number // 숫자일수도 문자일수도 ... 

let readonly alphanumber : string // 읽기 전용 타입

let fruits: string[] = ['Apple', 'Banana', 'Mango']; // 단일 타입으로 이루어진 Array 

let array: (string | number)[] = ['Apple', 1, 2, 'Banana', 'Mango', 3]; // 두개 이상의 타입으로 구성가능  

let tuple: [string, number] = ['1',2] //정해진 타입의 고정된 길이(length) 배열

enum Gender { // 열거형 타입
 Male,
 Female
}

let a: any = 123; // 어떤 타입이든 가능 
let u: unknown = 123; // any 타입과 같이 어떤 타입이든 할당 가능 하지만, unknown은 다른 타입에느 할당 불가 

let obj: object = { // 객체 
 isBoolean:boolean = false,
 str:string = "hello"
}; 

// type alias 도 가능
type Types = {
   isBoolean:boolean;
   outofStock?: boolean; // Optional 
   update: (retryTimes: number) => void; // arrow function field 
   readonly body: string; // 읽기 전용
   [key: string]: string;  // ??? 이건 뭔지 모르겠네요 ㅠ 
};


interface UserInterface { // 인터페이스
 name: string,
 age: number
}

let user1: UserInterface = { // 반복적으로 사용할시 유용
 name: 'Paul',
 age: 21
};

// 함수타입 
function hello(msg: string): void { // 함수의 parameter에도 타입 지정 가능 및 반환 값의 타입도 지정 가능 
 console.log(`Hello ${msg}`);
}

function hello(msg: string): Never { // 절대 발생하지 않을 값 = Never
 throw new Error(message);
}

// 클래스 타입
class User implements UserInterface {
 constructor(public name: string) {}
 getName() {
   return this.name;
 }
}


2. Refining Types

추가적으로 타입 선언과 더불어 해당 타입에 대한 추가적인 규제를 타입스크립트가 제공합니다. 저는 이를 3가지로 크게 정리하였습니다.

A. 타입 추론 (Type Inference)


let str = "Hello"; 
str = 1 // TypeError !!! 

위와 같이 str에는 명시적인 타입이 지정되지 않고, hello 라는 문자열 값이 할당되었습니다. 그리하여, 타입스크립트는 str 변수 타입을 문자열으로 추론하여 숫자 1을 재할당 하려 할때 에러가 나오게 됩니다.

B. 타입 단언 (Type Assertions)


interface User {
    type: 'user';
    name: string;
    age: number;
    occupation: string;
}

interface Admin {
    type: 'admin';
    name: string;
    age: number;
    role: string;
}

export type Person = User | Admin;

export function logPerson(person: Person) {
    let additionalInformation: string = '';
    if (isAdmin(person)) {
        additionalInformation = (person as Admin).role;
    }
    if (isUser(person)) {
        additionalInformation = (person as User).occupation;
    }
    console.log(` - ${person.name}, ${person.age}, ${additionalInformation}`);
}

위의 코드를 보면 person as Adminperson as User 라는 코드으로 각 User와 Admin Interface에 존재하는 고유 필드명을 사용할 수 있게됩니다. 이렇게 타입을 단언하지 않고 person에서 role이나 occupation 필드명을 참조할 경우 에러가 발생됩니다.

B # Not Null 단언 선언


// Non-null assertion operator
function returnX(x: number | null | undefined) {
  return x!.toFixed(2);
}

자료를 찾아보니 변수 뒤에 "!"을 추가함으로 해당 변수가 Null이 아니라고 단언 할 수도 있네요

C. 타입 가드 (Type Guard)

interface User {
    type: 'user';
    name: string;
    age: number;
    occupation: string;
}

interface Admin {
    type: 'admin';
    name: string;
    age: number;
    role: string;
}

export type Person = User | Admin;

export const persons: Person[] = [
    { type: 'user', name: 'Max Mustermann', age: 25, occupation: 'Chimney sweep' },
    { type: 'admin', name: 'Jane Doe', age: 32, role: 'Administrator' },
    { type: 'user', name: 'Kate Müller', age: 23, occupation: 'Astronaut' },
    { type: 'admin', name: 'Bruce Willis', age: 64, role: 'World saver' }
];

export function isAdmin(person: Person) : person is Admin{
    return person.type === 'admin';
}

export function isUser(person: Person) : person is User{
    return person.type === 'user';
}

export function logPerson(person: Person) {
    let additionalInformation: string = '';
    if (isAdmin(person)) {
        additionalInformation = (person as Admin).role;
    }
    if (isUser(person)) {
        additionalInformation = (person as User).occupation;
    }
    console.log(` - ${person.name}, ${person.age}, ${additionalInformation}`);
}

위의 코드에서 isAdminisUser 함수의 반환값을 Admin 과 User 라고 명시적으로 방어하는 것을 확인 할 수 있습니다. 위의 타입 단언 (Type Assertion)과 더불어 같이 사용하면 유용하게 사용될 듯 싶어서 예제코드를 가져와보았습니다. 이 외에도, js에서도 사용되던 instanceof, in, typeof 도 사용할 수 있겠네요.


3. Union Types

위의 예제들에서 간간히 보였던 타입 결합 입니다. 두 가지 이상의 타입들을 한가지 변수에 할당 할 수 있습니다.


function printStatusCode(code: string | number) {
  console.log(`My status code is ${code}.`)
}
printStatusCode(404);
printStatusCode('404');

하지만, 이렇게 두 가지 이상의 타입들을 사용 할 경우, 타입 가드 혹은 타입 가드를 사용하지 않으면 모든 타입에 유효한 함수 및 필드명만 사용할 수 있습니다. 아닐경우 아래와 같이 에러가 생깁니다.

function printStatusCode(code: string | number) {
  console.log(`My status code is ${code.toUpperCase()}.`) // error: Property 'toUpperCase' does not exist ontype 'string | number'.
  Property 'toUpperCase' does not exist on type 'number'
}

4. Merged Types

위의 Union Type이 두 가지 이상의 타입을 결합하는 것이라면, Merged type 같은 경우는 같은 이름의 Interface / Namespace 등을 여러변 선언하여 필드를 병합하는 것을 말합니다.


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

위와 같이 Box라는 인터페이스는 원래 heightwidth라는 필드명만 있었지만, scale이라는 새로운 필드명이 새로 생기는 것을 알 수있습니다.

interface Box {
  height: number;
  width: number;
}
interface Box {
  	scale: number;
    width: string; // Error: 'width' was also declared here.
}

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

하지만, 착각하면 안되는 것이 기존의 필드명과 같은 필드명이 존재할 경우, 값을 재 할당하는 것이 아니기때문에 이 점을 유의하면 좋겠네요

profile
웹 개발자,김승재입니다 !

0개의 댓글