공부

타입스크립트

출처 heropy.blog/2020/01/27/typescript/

 

한눈에 보는 타입스크립트(updated)

타입스크립트는 Microsoft에서 개발하고 유지/관리하는 Apache 라이센스가 부여된 오픈 소스로, 자바스크립트에 강한 타입 시스템을 적용해 대부분의 에러를 컴파일 환경에서 코드를 입력하는 동안

heropy.blog

 

타입스크립트를 쓰는 이유

 

- C#, JAVA 같은 체계적이고 정제된 언어에서 사용하는 강타입 시스템은 높은 가독성과 코드 품질등을 제공할 수 있고 런타임이 아닌 컴파일 환경에서 에러가 발생해 치명적인 오류들을 더욱더 쉽게 잡아낼 수 있다.

 

- 반면 자바스크립트는 약타입 언어로 비교적 유연하게 개발할 수 있는 환경을 제공하지만 런타임 환경에서 쉽게 에러가 발생할 수 있다는 단점을 가진다.

 

- 타입스크립트는 이러한 자바스크립트에 강타입 시스템을 적용해 대부분의 에러를 컴파일 환경에서 코드를 입력하는 동안 체크할 수 있다.

 


사용법

 

- ts 확장자를 가진 파일로 작성할 수 있고, 작성 후 타입스크립트 컴파일러를 통해 자바스크립트 파일로 컴파일하여 사용하게 된다.

tsc sample.ts
#compiled to 'sample.js'

기능

 

- 크로스 플랫폼 지원 : 자바스크립트가 실행되는 모든 플랫폼에서 사용 가능하다.

- 객체 지향 언어 : 클래스, 인터페이스, 모듈 등의 강력한 기능을 제공하며, 순수한 객체 지향 코드를 작성할 수 있다.

- 정적 타입 : 정적 타입을 사용하기 때문에 코드를 입력하는 동안에 오류를 체크할 수 있다.(에디터의 도움이 필요함)

- DOM 제어 : 자바스크립트와 같이 DOM을 제어해 요소를 추가하거나 삭제할 수 있다.

- 최신 ECMAScript 기능 지원 : ES6 이상의 최신 자바스크립트 문법 지원

 


타입 기본(Types)

 

타입 지정

 

- 타입스크립트는 일반 변수, 매개 변수, 객체 속성 등이 : TYPE과 같은 형태로 타입을 지정할 수 있다.

function add(a: number, b: number) {
  return a + b
}
const sum: number = add(1, 2)
console.log(sum) // 3

위의 코드에서 매개 변수 a, b를 number타입으로 지정했고 그렇게 실행된 반환 값은 숫자로 추론되기 때문에 sum도 number타입으로 지정함.

 

만약 sum에 string타입을 지정한다면 코드를 작성하는 시점에서 에러가 발생한다.

 


타입 종류

 

- boolean

 

- number

 

- string

 

- array. 배열은 두가지 방법으로 타입을 선언할 수 있다.

const fruits1: string[] = ['Apple', 'Banana'];
const fruits2: Array<string> = ['Apple', 'Banana'];

//유니언 타입(다중 타입)의 '문자열과 숫자를 동시에 가지는 배열'도 선언할 수 있다.
const unionArr: (string | number)[] = ['Apple', 1, 2];

//배열이 가지는 항목의 값을 단언할 수 없다면 any를 사용할 수 있다.
const someArr: any[] = [0, 1, [], {}, 'banana', false];

//인터페이스나 커스텀 타입을 사용할 수도 있다.
interface IUser {
  name: string
  age: number
}

const userArr: IUser[] = [
  { name: "Neo", age: 24 },
  { name: "Evan", age: 33 },
]

//readonly 키워드나 ReadonlyArray 타입을 사용하면 읽기전용배열을 만들 수 있다.
let arrA : readonly number[] = [1, 2, 3];
let arrB : ReadonlyArray<number> = [1, 2, 3];

 

- tuple : 정해진 타입의 고정된 길이의 배열을 표현한다.

let tuple: [string, number];
tuple = ['a', 1];
tuple = ['a', 1, 2]; //Error - TS2322
tuple = [1, 'a']; // Error - TS2322

 

튜플로 2차원 배열을 사용할수도 잇다.

let users: [number, string][];

users = [[1, 'Neo'], [2, 'Evan'], [3, 'Ken']];

 

튜플의 정해진 타입의 고정된 길이 특성은 할당에만 국한된다. 이후 push나 splice로 튜플을 변경시킬 수 있다.

 

 

- 열거형 Enum

enum Week {
  Sun,
  Mon,
  Tue,
  Wed,
  Thu,
  Fri,
  Sat,
}

console.log(Week.Mon); // 1
console.log(Week['1']); // Mon
console.log(Week); 
/*{
  '0': 'Sun',
  '1': 'Mon',
  '2': 'Tue',
  '3': 'Wed',
  '4': 'Thu',
  '5': 'Fri',
  '6': 'Sat',
  Sun: 0,
  Mon: 1,
  Tue: 2,
  Wed: 3,
  Thu: 4,
  Fri: 5,
  Sat: 6
}*/

Enum에 넣은 숫자 혹은 문자열 값의 집합에 0부터 1씩 증가하는 값을 부여한다.

중간에 수동으로 값을 변경한다면, 값을 변경한 부분부터 다시 1씩 증가한다.

역방향 매핑을 지원한다.

 

 

- any 모든타입 

일반적인 자바스크립트 변수와 동일하다.

외부 자원을 활용해 개발할 때 불가피하게 타입을 단언할 수 없는 경우 유용하다.

 

- Unknown 알수없는 타입

Any와 같이 최상위 타입인 Unknown은 알 수 없는 타입을 의미함. Any와 같이 어떤 타입의 값도 할당할 수 있지만, Unknown을 다른 타입에는 할당할 수 없다.

 

- 객체 Object

기복적으로 typeof 연산자가 'object'를 반환하는 모든 타입을 나타낸다.

객체, 배열, 함수, Date 모두 가능함. 여러 타입의 상위타입이기 때문에 object 타입 자체는 그다지 유용하지 않다.

보다 정확하게 타입 지정을 하기 위해 객체 속성들에 대한 타입을 개별적으로 지정할 수 있다.

let userA: {name: string, age: number} = {
  name: 'kim',
  age: 20
}

반복적인 사용을 원한다면 interface나 type을 사용하라

 

 

- Null, Undefined

기본적으로 Null과 Undefined는 모든 타입의 하위 타입으로, 다음과 같이 모든 타입에 할당할 수 있다.

let num: number = undefined;
let str: string = null;
let obj: { a: 1, b: false } = undefined;
let arr: any[] = null;
let und: undefined = null;
let nul: null = undefined;
let voi: void = null;

 

- void

일반적으로 값을 반환하지 않는 함수에서 사용한다.(실제로는 값을 반환하지 않는 함수는 undefined를 반환함. 근데 함수의 타입으로 undefiend를 지정하면 에러가난다)

 

- Never

절대 발생하지 않을 값을 나타내며, 어떠한 타입도 적용할 수 없다.

function error(message: string): never {
  throw new Error(message);
}

return undefined를 해도 안됨. 함수내에 에러 코드를 지우면 'never'를 반환하는 함수에는 엔드포인트가 연결되면 안된다고 한다.

검색해보니 무조건 에러를 뱉는 에러함수나 무한루프에서 빠져나올 수 없는 함수만 가능한 것 같다.

 

 

- 유니언 union

2개 이상의 타입을 허용하는 경우, 이를 유니언이라고 함

let union: (string | number);
union = 'hi';
union = 321;
union = true; //error

 

 

- 인터섹션 intersection

&를 사용해 두개 이상의 타입을 조합하는 경우를 인터섹션이라고 한다.

새로운 타입을 생성하지않고 기존의 타입을 조합할 수 잇기 때문에 유용하지만 자주 쓰진 않는다.

interface IUser {
  name: string
  age: number
}
interface IValidation {
  isValid: boolean
}

const neo : IUser & IValidation = {
  name: 'Neo',
  age: 25,
  isValid: true
}

 

- 함수

화살표 함수를 이용해 타입을 지정할 수 있다.

인수의 타입과 반환값의 타입을 입력한다.

let func: (arg1: number, arg2: number) => number
func = (x, y) => x + y

//인수가 없고 반환도 없는 경우
let func2: () => void
func2 = () => {
  console.log("hi")
}

 

 


타입 추론

 

명시적으로 타입 선언이 되어있지 않은 경우, 타입스크립트는 타입을 추론해 제공한다.

 

let num = 12; //여기서 num: number로 추론한다.
num = 'Hello'; //number로 추론된 값에 string을 넣어서 에러가 발생한다.

 

타입스크립트가 타입을 추론하는 경우 - 초기화된 변수 / 기본값이 설정된 매개 변수 / 반환 값이 있는 함수

 


타입단언

 

타입스크립트가 타입 추론을 통해 판단할 수 있는 타입의 범주를 넘는 경우, 직접 타입을 지정하여 더 이상 추론하지 않도록 지시할 수 있다.

이를 타입 단언이라고 하며, 이는 프로그래머가 타입스크립트보다 타입에 대해 더 잘 이해하고 있는 상황을 의미한다.

 

const func = (val: string | number, isNumber: boolean){
  if(isNumber){
    (val as number).toFixed(2);
  }
}

위의 경우 isNumber는 val이 숫자인지 테스트하는 함수이므로 isNumber가 true이면 val은 number타입일 것을 프로그래머는 알 수 있지만 컴퓨터는 모른다. 위의 경우에 val.toFixed(2); 를 하면 val이 string 타입일 수 있으니 컴퓨터는 에러를 뱉는다.

그래서 저 상황에선 val이 number 타입이라고 타입단언을 했다.

<number>val 이라는 문법으로 쓸 수도 있긴한데 이 방법은 jsx를 사용하는 경우 특정 구문 파싱에서 문제가 발생할 수 있고 .tsx파일에서는 전혀 사용할 수 없다고 함. (tsx는 타입스크립트로 리액트 컴포넌트 만들 경우 이 확장자를 쓴다고 함)

 

 

- Non-null 단언 연산자

 

!를 사용하는 Non-null 단언 연산자를 통해 피연산자가 null이나 undefined가 아님을 단언할 수 있는데, 변수나 속성에서 간단하게 쓸 수 있기 때문에 유용하다.

const func = (x : number | null | undefined) {
	return x!.toFixed(2)
}

 

 


타입 가드

 

function someFunc(val: string | number, isNumber: boolean) {
  if (isNumber) {
    (val as number).toFixed(2)
    isNaN(val as number);
  } else {
    (val as string).split(' ');
    (val as string).toUpperCase();
    (val as string).length;
  }
}

 

위 예제와 같이 val의 타입을 매번 보장하기 위해 타입 단언을 여러 번 사용하게 되는 경우에 타입가드를 쓴다.

타입가드는 추론 가능한 특정 범위에서 타입을 보장할 수 있다.

타입가드는 Name is TYPE 형태의 타입 술부를 반환 타입으로 명시한 함수이다.

 

//타입 가드 함수
function isNumber(val: string | number): val is number {
  return typeof val === "number"
}

function someFunc(val: string | number) {
  if (isNumber(val)) {
    val.toFixed(2)
    isNaN(val)
  } else {
    val.split(" ")
    val.toUpperCase()
    val.length
  }
}

 

위의 경우 처럼 타입가드 함수 대신 typeof, in, instanceof 연산자를 직접 사용하는 타입가드 방식도 있다.

 

// typeof를 쓰는 경우
function someFunc(val: string | number) {
  if (typeof val === "number") {
    val.toFixed(2)
    isNaN(val)
  } else {
    val.split(" ")
    val.toUpperCase()
    val.length
  }
}

// in을 쓰는 경우. in을 쓰려면 val이 any타입이어야한다.
function someFunc2(val: any) {
  if ("toFixed" in val) {
    val.toFixed(2)
    isNaN(val)
  } else if ("split" in val) {
    val.split(" ")
    val.toUpperCase()
    val.length
  }
}

// instanceof 연산자를 사용하는 경우
class Cat {
  meow() {}
}
class Dog {
  woof() {}
}
function sounds(ani: Cat | Dog) {
  if (ani instanceof Cat) {
    ani.meow()
  } else {
    ani.woof()
  }
}

 

별도의 추상화없이 연산자로 타입가드를 사용할 수 있다.

 

 

 

미완

'공부' 카테고리의 다른 글

lazy loading  (0) 2020.12.08
프레젠테이션/컨테이너 컴포넌트  (0) 2020.12.08
리액트  (0) 2020.12.06
보안(짧음)  (0) 2020.12.04
플로이드 와샬  (0) 2020.12.04