Typescript에 대해서

puka·2022년 9월 13일
post-thumbnail

typescript란?

  • 타입스크립트는 자바스크립트에 타입을 부여한 언어이다.
  • 자바스크립트의 확장된 언어로도 본다.

typescript의 특징

  • 타입스크립트는 정적 타이핑 언어로 컴파일에 시간이 들지만, 컴파일 이후 동작은 자바스크립트와 같다.
    (타입스크립트는 자바스크립트를 대체한다기 보다, 보완하는 역할을 담당한다.) < == > 자바스크립트는 동적 타이핑 언어로 컴파일에 시간을 들이지 않고, 동적 타이핑을 수행하기 때문에 런타임시 속도가 빠르다.

typescript를 쓰는 이유

if(typeof text !== 'string') throw '문자열 아님';

자바스크립트는 위와 같은 예외 처리로 검사를 해야한다. 하지만 if문을 이용한 타입 검사가 많아질수록 런타임에 불필요한 타입 검사를 하게 된다.
타입스크립트를 사용한다면 이러한 자바스크립트의 단점?을 보완할 수 있다.

에러의 사전 방지

//add.js
function sum(a,b) {
	return a + b;
};

sum(10, 20); //30
sum('10', '20') //1020
//add.ts
function sum(a:number, b:number){
	return a + b;
};

sum('10','20'); // Error: '10'은 number에 할당될 수 없습니다.

javascript로 작성하였을 때, 인자로 string이 들어가 의도하지 않은 코드가 동작한 것을 알 수 있다.

ts에서 인자를 동일하게 string으로 적용할 경우 typescript의 오류를 확인할 수 있다.

컴파일 단계에서 오류를 포착할 수 있고 명시적인 정적타입 지정은 개발자의 의도를 명확하게 코드로 기술할 수 있다. 이는 코드의 가독성을 높이고 예측할 수 있게 하며 디버깅을 쉽게 한다.

interface VS type

interface PersonInterface {
  name: string
  age: number
}

const me1: PersonInterface = {
  name: 'rain',
  age: 20,
}

type PersonType = {
  name: string
  age: number
}

const me2: PersonType = {
  name: 'rain',
  age: 30,
}

1.interface는 extends 키워드로 확장할 수 있다.


//interface 확장 방법
interface PeopleInterface {
  name: string
  age: number
}

interface StudentInterface extends PeopleInterface {
  school: string
}

//type의 확장 방법

type PeopleType = {
  name: string
  age: number
}

type StudentType = PeopleType & {
  school: string
}

2. interface는 선언적 확장이 가능하다.

여기서 두개의 확실한 차이는 type은 새로운 속성을 추가하기 위해서 다시 같은 이름으로 선언할 수 없고, interface는 항상 선언적 확장이 가능하다는 것이다.

// interface

interface Window {
  title: string
}

interface Window {
  ts: TypeScriptAPI
}

// 같은 interface 명으로 Window를 다시 만든다면, 자동으로 확장이 된다.

const src = 'const a = "Hello World"'
window.ts.transpileModule(src, {})

//type 

type Window = {
  title: string
}

type Window = {
  ts: TypeScriptAPI
}

// Error: Duplicate identifier 'Window'.
// 타입은 안된다.

3. interface는 객체에만 사용이 가능하다.

interface FooInterface {
  value: string
}

type FooType = {
  value: string
}

type FooOnlyString = string
type FooTypeNumber = number

interface fooOnlyString = string (x)

4. type은 computed value의 사용이 가능하지만 interface는 불가능하다.

type names = 'firstName' | 'lastName'

type NameTypes = {
  [key in names]: string
}

const yc: NameTypes = { firstName: 'hi', lastName: 'yc' }

interface NameInterface {
  // error
  [key in names]: string
}

5.결론

interface는 객체의 타입을 만들기 위한 것이다.

type의 경우 원시 타입이 올 수 있다. type의 경우merge하게 되는 경우 재귀적으로 순회하며 속성을 merge한다고 한다.

type type2 = { a: 1 } & { b: 2 } // 잘 머지됨
type type3 = { a: 1; b: 2 } & { b: 3 } // resolved to `never`

const t2: type2 = { a: 1, b: 2 } // good
const t3: type3 = { a: 1, b: 3 } // Type 'number' is not assignable to type 'never'.(2322)
const t3: type3 = { a: 1, b: 2 } // Type 'number' is not assignable to type 'never'.(2322)

그러므로 타입 간 속성을 머지 할 경우, 어차피 객체에서만 쓰는 용도라면, interface를 쓰는 것이 훨씬 낫다고도 한다. 또한 공식 문서에서도 interface 사용을 권장한다고 한다. 기본적으로 인터페이스로 표현할 수 있는 모든 타입은 인터페이스로 표현하고, 기본 타입에 새로운 이름을 붙이고 싶거나 유니온 타입을 명명하고 싶은 경우 등 인터페이스의 능력 밖인 부분에서만 타입 별칭을 사용하는 것이 좋다.

컴포넌트에서 함수 타입의 props 받아오기

src/Greetings.tsx

import React from 'react';

interface GreetingsProps {
  name: string;
  mark: string;
  optional?: string;
  onClick: (name: string) => void; // 아무것도 리턴하지 않는다는 함수를 의미합니다.
};

function Greetings({ name, mark, optional, onClick }: GreetingsProps) {
  const handleClick = () => onClick(name);
  return (
    <div>
      Hello, {name} {mark}
      {optional && <p>{optional}</p>}
      <div>
        <button onClick={handleClick}>Click Me</button>
      </div>
    </div>
  );
}

Greetings.defaultProps = {
  mark: '!'
};

export default Greetings;

src/App.js

import React from 'react';
import Greetings from './Greetings';

const App: React.FC = () => {
  const onClick = (name: string) => {
    console.log(`${name} says hello`);
  };
  return <Greetings name="Hello" onClick={onClick} />;
};

export default App;

generics

interface Backpack<Type> {
  add: (obj: Type) => void;
  get: () => Type;
}

// This line is a shortcut to tell TypeScript there is a
// constant called `backpack`, and to not worry about where it came from.
declare const backpack: Backpack<string>;

// object is a string, because we declared it above as the variable part of Backpack.
const object = backpack.get();

// Since the backpack variable is a string, you can't pass a number to the add function.
backpack.add(23);
Argument of type 'number' is not assignable to parameter of type 'string'.

main.d.ts

declare namespace MainType {
  type VolumeData = {
    searchingDate: string;
    volume: MainAllVolume[];
    volume_down: MainDownUp[];
    volume_up: MainDownUp[];
    volume_bar: KapiVolumeBar[];
  };

  type PriceData = {
    price: MainPrice[];
    price_down: MainPrice[];
    price_up: MainDownUp[];
  };
};

export default MainType;

Main.tsx

import React from "react";
import MainType from "../apiclient/types/main";

type State = {
  volumeData: MainType.VolumeData | null;
  priceData: MainType.PriceData | null;
};

export default class Main extends React.Component<{}, State> {
  state: State = {
    volumeData: null,
    priceData: null,
  };

  ...
}

참고 사이트

0개의 댓글