인프런 타입스크립트  기초를 통해 공부중이며 제로초님의 유료강좌입니다.

코드를 통으로 가져오지는 않겠습니다.(내가 글을 쓰면서 복습하는게 목적이기때문에 필요한 부분만)

 

타입스크립트에 익숙하지않다면 자바스크립트로 먼저 코드를 작성하고 그 후에 타입스크립트로 변환하도록하자.

 

 

타입스크립트 (Typescript)

 

 

인터페이스(interface)란?

"

인터페이스는 변수나 함수, 클래스와 같은 객체에서 사용한다.

여러가지 타입을 갖는 프로퍼티로 이루어지며 새로운 타입을 정의할 수 있다.

인터페이스에 선언된 프로퍼티나 메소드를 통해 구현을 강제할 수 있으며 이것을 통해 일관성을 유지할 수 있도록 한다. 인터페이스는 클래스와 유사하나 직접 인스턴스를 생성할 수 없다. 또한 모든 메소드는 추상 메소드이다.

"

 

인터페이스로 변환하는 예제를 확인해보자.

const test = {
	a : 1,
	b : 2
} as const;

// interface
interface TEST{
    a : 1,
    b : 2
}

이때 as const로 선언한 test와 interface로 선언한 test가 같은지를 생각해봐야한다.

as const로 선언 후 마우스를 포커싱해보면 vscode에서 readonly라고 뜨는것을 확인할 수 있다.

붙이지 않아도 값을 고정할 수 있지만 as const와 동일한 결과가 아닌 예제를 보자.

interface TEST{
    a : string
    b : string
}

interface TEST{
    readonly a : string
    b : string
}

위의 예제처럼 상수 값이 아닌 경우를 넣게될 경우에 as const와는 똑같다고할 수가 없다.

이때 readonly 키워드를 붙여주어 as const와 동일하게 만들어줄 수 있다.

interface TEST{
    readonly a : 1,
    readonly b : 2
}

 

인터페이스의 줄바꿈은 콤마, 세미콜론, 아무것도 붙이지 않는 것이 가능하다.

// 콤마
interface TEST{
    a : 1,
    b : 2
}

// 세미콜론
interface TEST{
    a : 1;
    b : 2;
}

// 줄바꿈
interface TEST{
    a : 1
    b : 2
}

 

 

인터페이스의 특징으로는 상속이 있다.

상속받을 경우 extends를 사용하면된다.

interface TEST{
    readonly a : string
    b : string
}

// 이렇게 하면 TEST를 상속받아 오는 것
interface Example extends TEST{
    // TEST의 a, b 타입을 가지고 있다.
}

// TEST를 상속받은 Example을 통해 타입 선언
const Hi : Example = {
    // TEST a, b 타입을 적용
};

인터페이스 객체의 틀을 잡고 상속하는 점이 클래스와 비슷하다고 할 수 있다.

또한 같은 이름의 인터페이스를 여러개 생성할 수 있다.

이럴 경우 인터페이스들은 하나로 합쳐진것과 동일하다.

타인이 만든 라이브러리의 인터페이스에 문제가 생겼을때 수정을 하기 위함이거나 확장을 해야할 경우에 사용한다.

interface TEST {}

// ....

interface TEST{}

 

 

type aliases는 타입으로 선언하는 것이다.

타입은 인터페이스처럼 같은 네이밍으로 선언했다고 해서 합쳐지는 것이 아니고 여러개 선언시에 에러가 난다.

type Test = {};

 

인터페이스와 타입 에일리어스의 선언 방식의 차이

interface TEST {};

// '=' 이 들어가야한다.
type TEST = {};

타입이 더 넓은 범위, 인터페이스는 객체일때 많이쓴다고 한다.

타입도 물론 인터페이스를 대체하여 객체를 만들수도 있다고 한다. (하지만 결국 선호도의 차이일 수도 있다고했다.)

 

타입으로 커스텀 타입을 생성할 수 있다.

새로운 test라는 타입을 생성하였다. test의 타입은 string 또는 number다.

type test = string | number

 

type을 객체로 사용하는 예시

type test = {
    a : 'a' 
} | string;

// string값, 객체 둘 다 사용가능하다.
const example : test = 'hello';
const example : test = {
    a : 'a'
};

 

객체로 사용할때에는 인터페이스를 사용하는게 좋다는데, 타입은 나중에 복잡해질 경우 사용하게 된다고 한다.

(위에 말했듯 선호도 일수도 있다고 했다.)

유니언이 필요할 경우에 타입을 쓴다.

type test = {
    //..
} | string;

 

 

keyof

위에서 선언한 key, value 값을 아래에서 타입으로 사용하기 위해서는 keyof를 통해 쓸 수 있다.

key 예시

interface TEST {
    readonly orange : 'orange',
    readonly apple : 'apple'
}

// TEST와 같은 키를 return 받는 함수.
function get(o) : keyof TEST{
    // ...
}

 

반대로 value 값 예시

interface TEST {
    readonly orange : 'orange',
    readonly apple : 'apple'
}

// 인자값 o가 TEST의 value 값과 동일해야할때
function get(o ㅣ TEST[keyof TEST]) : keyof TEST{
    // ...
}

 

만약 불가피하게 어떠한 값이 들어올 지 모르게되는 경우에는 넓은 범위로 선언해준다.

하지만 엄격하게 사용하는 것이 좋기때문에 지양하는 것이 좋다.

// [key : string] : number
interface Example{
    a : 2,
    b : 3,
    [key : string] : number;
}

const example : Example = {
    a : 2,
    b : 3,
    c : 100
}

 

lib.es5.d.ts파일을 확인해보면 Object.keys()는 반환값이 string[]으로 고정되어있다.

따라서 에러가 생기는 경우가있다.

이때는 타입스크립트가 스스로 범위를 좁게 잡지 생기지 못하기때문에 강제로 넓은 범위로 넓혀주는 것이 좋다.

function TEST (o : Example[keyof Example]) : keyof Example {
	// as ['a', 'b', 'c'] 강제로 선언
    return (Object.keys(test) as ['a', 'b', 'c']).find((k) => test[k] === o);
}

또한 위의 코드에서 find메서드를 사용했는데, 이 메서드는 ES6에서 사용할 수 있으므로 tsconfig.json을 수정해줘야한다.

{
    "compilerOptions" : {
        "strict" : true,
        
        // ES5, DOM이 기본값이므로 그 이상의 값을 다 입력해주자. 
        // 이 코드는 아직까지 줄이기가 힘들다고 했다.
        "lib" : ["ES5", "ES6", "ES2016", "ES2017", "ES2018", "ES2019", "ES2020", "DOM"]
    },
    "exclude" : ["*.js"]
}

 

또한 명령어 입력 시 tsconfig.json을 포함할 경우에 해당 파일이 무시되어 에러가나는 경우가 생기는데

이때 파일을 입력하지 않은채로 명령어를 입력해준다.

// cmd
npx tsc -w

 

find의 타입을 확인해보면 리턴 값이 T | undefined로 고정되어있는 것을 확인할 ㅜㅅ 있다.

이때는 아래와 같이 느낌표를 추가하여 해결할 수 있다.

느낌표를 입력하면 에러가 사라지는데, 느낌표는 타입시스템에서는 오류라고 말하지만 개발자가 그 코드를 보증하는 것이다. 이건 프로그래머의 재량이다. 느낌표가 없어서 실행이 안되는 경우들이 존재하는데 이때 사용할 수 있다. undefined나 null을 보증할 수 있다. (하지만 안쓰는 방법도 존재한다.)

function TEST (o : Example[keyof Example]) : keyof Example {
    return (Object.keys(test) as ['a', 'b', 'c']).find((k) => test[k] === o)!;
} 

 

 

this에러

화살표 함수가 아닐 경우에 발생하는 경우가 있는데, 이때 자바스크립트와 가장 크게 달라진다.

매개변수 첫번째 위치에 this 와 this의 타입을 선언해준다.

document.querySelectAll('button').forEach((btn) => {
    btn.addEventListener('click', function(this : HTMLButtonElement, e : Event){
            const my = this.textContent;
    });
});

타입스크립트는 html을 모르기 때문에 this.textContent가 string | null로 유추한다

lib.dom.d.ts파일을 보면 textContent에서 확인 할 수 있다.

 

타입스크립트가 스스로 추론하지 못하는 값들을 선언해줘야한다.

document.querySelectAll('button').forEach((btn) => {
    btn.addEventListener('click', function(this : HTMLButtonElement, e : Event){
       // TEST의 key값과 동일할 경우 as를 통해 타입 선언을 해준다.
       const my = this.textContent as keyof TEST;
   });
});

 

 

느낌표, if문, 꼼수를 통해 에러 보증하기

querySelector도 html을 인식하지 못하기 때문에 자바스크립트처럼 사용하면 에러가나게된다.

이때 lib.dom.d.ts파일에서 E | null로 유추하고 있음을 확인할 수 있다.

느낌표를 사용하여 에러를 막을 수 있다.

document.querySelector('#test')!.style.background = '';

또한 if문으로도 해결할 수 있다.

if문으로 감싸줄 경우 null이 아님을 보증하기 때문에 에러를 방지할 수 있다.

if(document.querySelector('#test')){
  document.querySelector('#test').style.background = '';
}

또 다른 방법으로 꼼수를 쓸 수도 있다.

근데 이렇게 하느니 느낌표를 쓰라더라.

if(!test){
    throw new Error('nope')
}
return test;

 

그 후 querySelector뒤에 style에서 에러가 또 발생하는데, querySelector는 E | null 이었다.

이때 E는 Element를 상속받는데, E는 style이 존재하지 않는다고 뜬다.

이때 제네릭으로 해결할 수 있는데, (아직 안배웠다)

현재 할 수 있는 방법으로는 as를 통해 형변환을 하는 방법이다.

HTMLDivElement는 style이 존재하고 Element는 style이 없기때문에 HTMLDivElement로 형변환을 해주자.

if(document.querySelector('#test')){
  document.querySelector('#test') as HTMLDivElement.style.background = '';
}

 

아직배우지는 않았지만 제네릭으로 썼을때는 아래와 같이 사용할 수 있다.

또한 변수에 담지않을 경우 문자열이 같다고 인식하지 않기때문에 변수에 담아 중복코드를 방지해주자.

// (X)
if(document.querySelector<HTMLDivElement>('#test')){
  document.querySelector<HTMLDivElement>('#test').style.background = '';
}

// (O)
const test = document.querySelector<HTMLDivElement>('#test')

if(test){
  document.querySelector<HTMLDivElement>('#test').style.background = '';
}

 

타입스크립트의 엄격함때문에 형변환을 해야하는 경우도 있다.

textContent는 string이기때문에 아래와 같이 형변환을 해줘야한다.

(document.querySelector('#test') as HTMLDivElement).textContent = String(point);

 

 

 

+ Recent posts