FrontEnd

[이펙티브 타입스크립트] 7장 코드를 작성하고 실행하기

mingg123 2023. 6. 20. 21:33

주요 내용

  • 자바스크립트와 타입스크립트 호환 관련하여 지양해야할 점
  • 정보 은닉 하는 방법(클로저)
  • 소스맵 정의


아이템53 타입스크립트 기능보다는 ECMAScript 기능을 사용하기

 

타입스크립트와 자바스크립트간의 호환성문제로 유의해야 하는 것들

  • enum
const enum Flavor {
    V = 0,
    C = 1,
    S = 2,
}

function scoop(flavor: Flavor)

scoop('vanilla'); // 자바스크립트에서는 정상

scoop('vanilla'); // 타입스크립트에서는 에러 'vanilla' 형식은 'Flavor' 형식의 매개변수에 할당될 수 없습니다

자바스크립트와 타입스크립트에서 동작이 다르기때문에 문자열 열거형은 사용하지 않는 것이 좋다.

열거형 대신 리터럴 타입의 유니온을 사용하자.

 

type Flavor = 'vanilla' | 'chocolate' | 'strawberry';

let flavor: Flavor = 'chocolate'; //정상

function scoop(flavor: Flavor) {
    if (flavor === 'v
    // 자동완성이 'vanilla'를 추천합니다.
    }

자바스크립트와 호환도 되고, 열거형처럼 자동완성 기능을 사용할 수 있다.

 

  • 매개변수 속성
    • 클래스 초기화시 생성자와 매개변수 사용 문법(JS/TS)
// 자바스크립트
class Person {
    name: string;
    constructor(name: string){
    	this.name = name;
    }
}


// 타입스크립트 
class Person {
	constructor(public name: string){}
}

해당 타입스크립트 코드의 문제점 

  • 코드가 줄어들지만, 매개변수 속성은 늘어나는 문법임
  • 매개변수 속성과 일반 속성(JS 예시)를 섞어서 사용하면 더 혼란스러워짐

 

  • 트리플 슬래시 임포트
    • 타입스크립트 초창기 시절 모듈 시스템(임포트 하기 위한) 
namespace foo {
function bar() {}
}
/// <reference path="other.ts"/>

 

 

  • 데코레이터
    • 클래스, 메소드, 속성등에 어노테이션을 붙임으로써 기능을 추가하는데 사용할 수 있다.
class Greeter {
    greeting: string;
    constructor(message: string) {
    	this.greeting = message;
    }
    @logged
    greet() {
    return "Hello, " + this.greeting;
    }
}

function logged(target: any, name: string, descriptor: PropertyDescriptor) {
    const fn = target[name];
    descriptor.value = function() {
        console.log('Calling ${name}');
        return fn.apply(this, arguments);
    };
}
  • 타입스크립트 코드에서 모든 타입 정보를 제거하면 자바스크립트가 되지만 enum, 매개변수 속성, 트리플 슬래시 임포트, 데코리에터는 타입 정보를 제거한다고 자바스크립트가 되지 않는다.
  • 타입스크립트의 역할을 명확하게 하려면, enum, 매개변수 속성, 트리플 슬레시 임포트, 데코레이터는 지양하는게 좋음. 

 

아이템54 객체를 순회하는 노하우

우리가 개발하면서 일상에서 자주 겪는 ts 에러이다.

const obj= {
    one: 'uno',
    two: 'dos',
    three: 'tres',
}

for(const k in obj) {
	const v = obj[k];  // - obj 에 인덱스 시그니처가 없기 때문에 엘리먼트는 암시적으로 'any' 타입입니다.
}

 

해결 방안

const obj= {
    one: 'uno',
    two: 'dos',
    three: 'tres',
}


// 해결방안1
let k: keyof typeof obj;
for( k in obj) {
    const v = obj[k];
}

// 해결방안2
function foo(abc: ABC) {
    for (const [k, v] of Object.entries(abc)) {
    k // string 타입
    v // any 타입
    }
}
  • 해결방안1) K의 타입을 구체적으로 알려줌
  • 해결방안2) Object.entries 사용

 

 

아이템55 DOM 계층 구조 이해하기

아래 코드는 타입스크립트에서 수많은 오류가 발생함 

function handleDrag(eDown: Event) {
    const targetEl = eDown.currentTarget;
    ta rgetEl.classList.add(‘dragging1);
    const dragStart = [eDown-clientX, eDown.clientY];
    const handleUp = (ellp: Event) => {
        targetEl.classList.remove('dragging');
        targetEl.removeEventListener('mouseup', handleUp);
        const dragEnd = [ellp.clientX, eUp-dientY];
    console.log('dx, dy =', [0, l].map(i => dragEnd[i] - dragStart[i]));
}
	targetEl.addEventListener('mouseup', handleUp);
}
const div = document.getElementByld('surface');
div.addEventListener('mousedown', handleDrag);

 

이럴경우에는 타입 단언문을 사용한다.

 

HTMLParagraphElement는 HTMLElement의 서브타입

HTMLElement는 Element 의 서브타입

Element는 Node의 서브타입

Node는 EventTarget 의 서브타입

 

아이템56 정보를 감추는 목적으로 private 사용하지 않기

class Foo {
	_private = 'secret123';
}

class Diary {
	private secret = 'cheated on my English test';
}
const diary = new Diary();
(diary as any).secret // 정상


클래스에서 _ 접두사를 이용하여 비공개 속성을 만들어서 사용하지만, 런타임에는 아무런 효력이 없을뿐더러 타입단언문을 사용하면 private에도 접근이 가능하다. 

 

정보를 은닉하기위해선 클로저를 사용해야 한다. 

클로저 예시

declare function hash(text: string): number;
    class Passwordchecker {

    checkPassword: (password: string) => boolean;

    constructor(passwordHash: number) {
        this.checkPassword = (password: string) => {
        return hash(password) === passwordHash;
        }
    }
}

const checker = new PasswordChecker(hash('s3cret'));
checker.checkPassword('s3cret'); // 결과는 true
  • 장점) Passwordchecker 생성자 외부에서 passwordHash 변수에 접근할 수 없다 -> 정보 은닉 성공
  • 단점) passwordHash에 접근하는 메서드 역시 내부에 작성되어야하고, 인스턴스가 생성될때마다 메서드의 복사본이 생성됨 -> 메모리 낭비 발생

 

 

현재 표준화 진행중인 정보 은닉 방법

class Passwordchecker {
    #passwordHash: number;
    constructor(passwordHash: number) {
    	this.#passwordHash = passwordHash;
    }
    checkPassword(password: string) {
    	return hash(password) === this.#passwordHash;
    }
}
const checker = new Passwordchecker(hash('s3cret1'));
checker.checkPassword('secret'); // 결과는 false
checker.checkPassword('s3cret'); // 결과는 true

이런 문법은 2021년도 기준으로 자바스크립트 표준3단계라고 함. 

 

아이템57 소스맵을 사용하여 타입스크립트 디버깅하기 

타입스크립트 코드를 실행한다 -> 타입스크립트 컴파일러가 생성한 자바스크립트 코드를 실행한다는 의미

변환된 자바스크립트코드는 복잡해서 디버깅하기 어려움 -> 이를 해결하기위해 Source Map 이 만들어짐

 

  • Source Map
    • 변환된 코드의 위치와 코드를 매핑함 
    • 원본코드가 그대로 유출되지 않도록 주의해야함 

아래는 소스맵 관련해서 자세하게 설명되어있는 좋은 글이 있어서 공유한다.

https://yozm.wishket.com/magazine/detail/2082/?utm_source=oneoneone 

 

소스 맵의 동작 원리는 무엇일까? | 요즘IT

실제로 디버깅을 해보면 브라우저에서 인식할 수 없는 파일이라고 해도 코드 라인의 위치를 정확히 짚어주는 것을 확인할 수 있습니다. 이것이 가능한 이유는 우리가 브라우저에게 원본 소스

yozm.wishket.com