ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 이펙티브 타입스크립트 아이템 1~3
    TypeScript 2023. 6. 2. 15:38
    728x90

    아이템1: 타입스크립트와 자바스크립트의 관계 이해하기

    TypeScript는 JavaScript의 상위 집합입니다.


    따라서 main.js를 main.ts로 변경하여도 달라지는 것은 없습니다. 이것은 JavaScript를 TypeScript로 마이그레이션하는데 큰 도움을 줍니다.
    다음 예제를 확인해보겠습니다.

    function greet(who: string) {
      console.log('Hello', who)
    }

    JavaScript는 who: string에 대해서 Unexcepted Token 에러를 발생시킵니다.

    function greet(who) {
      console.log('Hello', who)
    }

    하지만 위의 예제를 TypeScript에서는 에러가 발생하지 않습니다. 따라서 JavaScript의 상위집합이라고 할 수 있습니다.

     

    다음 예제를 JavaScript에서 돌려보겠습니다.

    let city="new york city"
    console.log(city.toUppercase())
    //타입스크립트인 경우 아래와 같은 문구가 보이게됩니다.
    //'toUppercase' 속성이 string 형식에 없습니다.
    //'toUpperCase' 를 사용하시겠습니까?

    string을 대문자로 바꿔주는 함수는 toUpperCase() 이므로 에러가 발생하지만 이유가 무엇인지 알기 힘듭니다. 하지만 typescript는 타입 추론을 통해서 city가 string임을 추론하기 때문에 string의 메소드인 toUpperCase 임을 유추하여 알려줄 수 있습니다.

     

    아래의 예제도 javascript에서 실행해보겠습니다.

    const states = [
    	{name: 'Alabama', capital: 'Montgomery'}.
    	{name: 'Alaska', capital: 'Juneau'}.
    ]
    for(const state of states) {
    	console.log(state.capitol)
    }
    
    //출력 결과
    //undefined
    //undefined

    typescript는 다음과 같이 오류를 찾아냅니다.

    for(const state of states) {
    	console.log(state.capitol) 
        //'capitol'속성이 state형식에 없습니다.
        //'capital'을 사용하시겠습니까?
    }

    과연 이 오류가 정확한 오류인가 생각했을 때, 우리가 states에 capitol 속성을 입력하고 싶었을 수 있기 때문에 정확한 오류라고 볼 수는 없습니다.따라서 아래와 같이 명시적인 type을 선언하여 보다 명확한 오류를 잡을 수 있게 하는 것이 좋습니다.

    interface State {
    	name: string
        capital: string
    }

    따라서 아래와 같은 벤 다이어그램이 나올 수 있습니다.

    typescript 타입 시스템은 전반적으로 자바스크립트의 동작을 모델링합니다. 하지만 자바스크립트에서 허용되지만 타입스크립트에서는 문제가 되는 경우도 있습니다. 이러한 문법의 엄격함은 취향 차이이므로 아래와 같은 상황이 익숙하다면 사용하지 않는게 나을 수 있습니다.

    const x = 2 + '3' // '23'
    const a = null + 7 // 7
    const b = [] + 12 // 12

     

     

    아이템2 : 타입스크립트 설정 이해하기

    아래의 코드가 타입 체커를 통과 할 수 있을까요?

    function add(a, b) {
    	return a + b
    }
    add(10, null)

    정답은 모릅니다. 입니다. 설정이 어떻게 되어있는지 모르기 때문입니다.

    tsconfig.js를 통해 엄격도를 설정하는 것이 좋습니다. 그래야 다른 동료 개발자들이 어떻게 타입스크립트를 사용할 계획인지 알기 쉽습니다. 설정 파일은 tsc --init을 실행하여 쉽게 생성할 수 있습니다.

    설정에서 가장 중요한 noImplicitAny와 strictNullChecks를 이해해 보겠습니다.

    noImplicitAny

    noImplicitAny는 변수들이 미리 정의된 타입을 가져야하는지 여부를 제어합니다.

    noImplicitAny가 켜져있지 않다면 아래의 코드는 다음과 같이 추론됩니다.

    function add (a, b) {
    // 변수 a, b는 any로 추론 됩니다.
    	return a + b
    }

    하지만 같은 코드에 noImplicitAny가 켜져있다면 오류가 됩니다. 반드시 어떠한 타입을 선언해 주어야 합니다.

    타입스크립트는 타입 정보를 가질 때 가장 효과적이기 때문에 noImplicitAny를 켜주는 것이 좋습니다.

    만약 javascript를 typescript로 마이그레이션 하고있다면 noImplcitAny를 끈 상태로 진행하면 됩니다.

    strictNullChecks

    strictNullChecks는 null과 undefined가 모든 타입에서 허용되는지 확인하는 설정입니다.

    strictNullChecks가 켜져있지 않다면 아래의 코드는 다음과 같이 추론됩니다.

    const x: number = null // null은 유효한 값입니다.

    켜져있다면 다음과 같습니다.

    const x: nmber = null // null형식은 number 형식에 할당할 수 없습니다.

    undefined도 null과 같습니다. 만약 null이나 undefined를 사용해야 한다면 명시해줘야 합니다.

    const x: number | null = null

     

    아이템3 : 코드 생성과 타입이 관계없음을 이해하기

    타입스크립트 컴파일러는 크게 두 가지 역할을 수행합니다.

    • 최신 타입스크립트/자바스크립트를 브라우저에서 동작할 수 있도록 구버전의 자바스크립트로 트랜스파일합니다.
    • 코드의 타입 오류를 체크합니다.

    여기서 중요한 것은 위 두 가지 역할은 독립적으로 수행된다는 것 입니다.

     

    타입 오류가 있는 코드도 컴파일이 가능하다.

    타입에 오류가 있는 코드도 컴파일이 가능합니다. 이 때문에 타입스크립트가 엉성한 언어로 보일 수 있지만 웹에선 이 부분이 도움이 됩니다. 어떤 코드에 문제가 발생했을 때 타입스크립트는 컴파일된 산출물을 생성하기 때문에 웹의 다른 부분을 테스트 할 수 있게 됩니다. 만약 오류가 있을 때 컴파일을 하고싶지 않다면 noEmitOnError옵션을 켜주면 됩니다.

    더보기

    타입스크립트를 사용하면서 코듣에 오류가 있을 때 "컴파일에 문제가 있다"라고 말하는 경우는 엄밀히 말하면 기술적으로 틀린 말입니다. 타입스크립트에서 컴파일은 코드의 생성 이기 때문에 유효한 자바스크립트로 변환 가능하다면 컴파일이 가능하고, 이 때 발생하는 타입 에러는 "타입 체크에 문제가 있다." 라고 보는 것이 맞습니다.

    런타임에는 타입 체크가 불가능합니다.

    interface Square {
    	width: number;
    }
    interface Rectangle extends Square {
    	height: number;
    }
    type Shape = Square | Rectangle;
    
    function calculateArea(shape: Shape) {
    	if(shape instanaceof Rectangle) {
        	//shape의 형식이 Rectangle인지 확인했는데 아래에선 값을 사용하므로 에러가 발생합니다.
        	return shape.width * shape.height
        } else {
        	return shape.width * shape.width
        }
    }

    instanceof는 런타임에 일어나지만 타입은 런타임 시점에 아무 역할도 할 수 없습니다. 실제로 타입스크립트가 자바스크립트로 컴파일되면 타입, 인텊이스, 타입 구문은 제거됩니다.

    shape 타입을 런타임에도 유지하려면 아래와 같은 방법을 사용할 수 있습니다.

    function calculateArea(shape: Shape) {
    	if('height' in shape) {
        	return shape.width * shape.height
        } else {
        	return shape.width * shape.width
        }
    }

    또 다른 방법으로는 태그 기법이 있습니다.

    interface Square {
    	kind: 'square'
        width: number
    }
    interface Rectangle {
    	kind: 'rectangle'
        height: number
        width: number
    }
    type Shape = Square | Rectangle
    
    function calculateArea(shape: Shape) {
    	if(shape.kind === 'rectangle') {
        	return shape.width * shape.height
        } else {
        	return shape.width * shape.width
        }
    }

     

    타입 연산은 런타임에 영향을 주지 않습니다.

    function asNumber(val: number | string) {
    	return val as number;
    }

    위 예제는 타입 체커는 통과 되지만 잘못된 방법입니다.

    여기서 as number는 타입 연산이고 런타임 동작에 아무 영향도 주지 않습니다. 값을 정제하기 위해선 아래의 방법을 사용하는게 좋습니다.

    function asNumber(val: number | string) {
    	return tyepof(val) === 'string'? Number(val) : val
    }

     

    런타임 타입은 선언된 타입과 다를 수 있습니다.

    function setLightSwitch(value: boolean) {
    	switch(value) {
        	case true:
            	turnLightOn()
                break
            case false:
            	turnLightOff)
                break
            default:
            	console.log('실행 될라나')
        }
    }

    위 코드에서 value는 boolean이므로 default 부분이 필요 없다고 느낄 수 있습니다.

    하지만 런타임 상황에 value가 서버로부터 문자열로 넘어온다면 default 부분이 실행될 수 있습니다.

    따라서 타입 체크가 이루어 졌더라도 선언된 타입이 언제든 바뀔 수 있다는 점을 명심해야 합니다.

     

    타입스크립트 타입으로는 함수를 오버로드할 수 없습니다.

    타입스크립트에서 타입은 런타임 동작과 무관하기 때문에 오버로딩이 불가능합니다. 따라서 여러개의 선언문은 작성할 수 있지만 구현체는 하나입니다.

    function add(a: number, b: number) { return a + b; }
          // ~~~ Duplicate function implementation
    function add(a: string, b: string) { return a + b; }
          // ~~~ Duplicate function implementation

     

    타입스크립트 타입은 런타임 성능에 영향을 주지 않습니다.

    타입과 타입 연산자는 자바스크립트로 변환되는 시점에(컴파일) 런타임의 성능에 아무런 영향을 주지 않습니다. 

     

     

     

    728x90

    'TypeScript' 카테고리의 다른 글

    [TypeScript] 선언병합  (1) 2022.09.23
    [TypeScript] 고급타입 - 2  (1) 2022.09.22
    [TypeScript] 고급타입-1  (0) 2022.09.21
    [TypeScript] 제네릭  (0) 2022.09.17
    [TypeScript] 열거형  (1) 2022.09.16

    댓글

Designed by Tistory.