타입스크립트 기본 타입

김동현·2022년 2월 24일
0

TypeScript

목록 보기
3/18
post-thumbnail

구조적 타이핑

자바스크립트는 본질적으로 "덕 타이핑" 기반입니다. 덕 타이핑이란 요구사항을 만족한다면 타입이 무엇이든 상관하지 않는 동작을 의미합니다.

타입스크립트는 자바스크립트의 상위 집합으로 덕 타이핑 특징을 그대로 갖고 있습니다. 타입스크립트에서는 이를 "구조적 타이핑"이라고 하며 런타임 이전에 오직 멤버만으로 타입을 관계시키는 방식을 사용합니다.

이는 객체가 어떤 타입에 부합하는 최소한의 특징을 가지고 있다면, 그냥 그 타입에 해당하는 것이라고 간주하는 것입니다.

위 코드에서 Person 인터페이스와 Students 인터페이스는 동일한 타입 간주됩니다.

두 타입간 이름은 서로 다르지만 인터페이스 "구조"가 동일하기 때문에 타입스크립트 컴파일러는 동일한 타입으로 간주하게 되어 에러를 발생시키지 않습니다.

타입은 "값의 집합"

구조적 타이핑을 따르는 타입스크립트의 타입은 "값의 집합, 범위"로 생각할 수 있습니다. 타입스크립트의 "타입 체커는 하나의 집합이 다른 집합의 부분 집합인지를 검사"하는 것으로 타입을 체크합니다.

서브 타입으로 만족되는 경우에는 타입이 서로 호환되어 타입 체커가 에러를 발생시키지 않게 됩니다. 즉, "상위 집합은 하위 집합을 허용"한다는 의미입니다.

위 코드에서 변수 p의 타입을 Person 인터페이스로 선언하고, Students 인터페이스 타입을 갖는 변수 s를 할당하더라도 타입스크립트 컴파일러는 에러를 발생시키지 않습니다.

이는 Students 인터페이스가 Person 타입의 서브 타입(부분 집합)으로 타입이 서로 호환되기 때문입니다.


위 그림은 타입스크립트 타입의 포함 관계(타입 계층)입니다.

"상위 타입은 하위 타입의 값을 가질 수 있으며" 이는 타입 체킹에서 에러를 발생시키지 않습니다.

위 그림은 tsconfig.json의 strictNullChecks 옵션이 true인 경우입니다.

Typescript Type

자바스크립트는 런타임에 타입이 결정되는 동적 타입 언어이며, 타입스크립트는 런타임 이전, 즉 개발 도중에 타입이 결정되는 "정적 타입" 언어입니다.

변수나 매개변수 이름 뒤에 ": type"을 작성하여 타입을 선언하고, 타입이 지정된 변수는 지정된 타입과 하위 타입의 값만을 할당받을 수 있습니다.

선언된 타입은 일반적으로 변경되지 않습니다.
변경되는 예외의 경우로는 타입 단언문, any 타입의 진화, 타입 좁히기가 존재합니다.


참고로 타입스크립트에서 원시 타입은 String, Number, Boolean이 아니라 string, number, boolean과 같이 모두 "소문자"를 사용합니다.
"대문자의 경우에는 래퍼 객체"를 나타내는 타입을 의미하게 됩니다.

타입을 선언하는 구문은 타입스크립트에서 도입된 기능이며 타입스크립트 컴파일러만이 이를 이해합니다. IDE(Visual Studio 등) 또한 타입스크립트를 이해하므로 타입스크립트에서 사용되는 특수 키워드를 이해하고 사용자에게 표시해줍니다.

하지만 브라우저는 오직 자바스크립트만을 이해하고 실행할 수 있기 때문에 타입스크립트로 작성한 파일을 자바스크립트 파일로 변환하는 트랜스파일 작업이 필요합니다. 이 작업은 타입스크립트 컴파일러를 통해 자바스크립트 파일로 변환이 가능합니다.

즉, 타입 선언과 같은 타입스크립트에서 사용되는 문법은 타입스크립트 컴파일러와 IDE만 이해할 수 있습니다.

타입스크립트 컴파일러가 컴파일하면 타입스크립트 구문들은 모두 제거가 됩니다. 즉, 런타임에 영향을 주지 않습니다.

string

기존 자바스크립트에서 문자열은 변수에 따로 타입을 지정하지 않고 문자열을 할당해주었습니다.

string 타입의 상위 타입은 unknown, any, String 타입이며 하위 타입은 string literal, any 타입이 존재합니다.

집합 관계 : unkown, any > "string" > string literal > any

즉, string 타입은 string, string literal, any 타입의 값만 할당받을 수 있습니다.

타입스크립트에서는 문자열만을 할당받는 변수를 선언할 때는 명시적으로 문자열 타입을 변수에 할당하기 위해 아래처럼 작성합니다.

str이라는 변수 뒤에 ": string"을 작성해줍니다. 그러면 이제 str이라는 변수는 문자열만을 할당받는 변수로 타입을 선언하였습니다.

이후 숫자나 불리언 등 다른 타입의 값으로 재할당하는 것이 불가능합니다.

number

자바스크립트에서의 number 타입처럼 정수형이나 실수형에 해당하는 특별한 타입은 없으며 하나의 타입으로 처리합니다.

타입스크립트에서 숫자를 할당받는 변수에 명시적으로 숫자 타입을 할당하기 위해서는 아래 처럼 작성합니다.

집합 관계 : unknown, any > "number" > number literal > any

number 타입은 number, number literal, any 타입의 값만 가질 수 있습니다.

즉, 변수 이름 뒤에 ": number"을 작성하여 num 변수가 number 타입의 값만을 할당받도록 지정합니다.

boolean

불리언 타입의 값을 할당받는 변수는 ": boolean"을 작성하여 불리언 타입을 지정합니다.

집합 관계 : unknown, any > "boolean" > boolean literal > any

any

포함 관계 측면에서 any 타입은 모든 타입의 "서브 타입"이 될 수 있고, 모든 타입의 "상위 타입"도 될 수 있습니다.

집합 관점에서 타입 시스템과 상충된다는 점을 알 수 있습니다. 즉, any 타입은 지양하는 것이 좋습니다.

포함 관계 : 모든 타입 > "any" > 모든 타입

변수를 선언할 때 타입을 선언하지 않고, 초기화 값도 지정하지 않은 경우 변수의 타입이 암묵적으로 any 타입으로 추론됩니다.

함수의 매개변수 또한 명시적으로 타입을 지정하지 않는 경우 any 타입으로 추론됩니다.

타입스크립트 컴파일러의 설정 파일인 tsconfig.json에서 "noImplicitAny"라는 옵션에 true로 지정한 경우 암묵적으로 추론된 타입이 any인 변수에 대해서 에러를 표시하게 됩니다.

any 타입의 문제점

any 타입의 사용은 "지양"해야 합니다. any 타입의 사용시 아래와 같은 문제점들이 발생하게 됩니다.

  • any 타입에는 타입 안정성이 없습니다.

  • any는 함수 시그니처를 무시합니다.

  • any 타입에는 자동원성 기능과 적절한 도움말이 제공되지 않습니다.

  • any 타입은 코드 리팩터링 때 버그를 감춥니다.

  • any는 타입 설계를 감춰버립니다.

  • any는 타입시스템의 신뢰도를 떨어뜨립니다.

즉, any 타입을 사용하면 타입 체커를 무력화시켜버리기 때문에 최대한 사용을 피하도록 하는 것이 좋습니다. any를 사용하는 것보다 unknown 타입을 사용하는 것이 낫습니다.

any 타입의 진화

일반적으로 타입스크립트의 타입은 변경되지 않습니다. 하지만 예외의 경우가 존재합니다. 바로 any 타입의 진화가 그에 해당됩니다.

any 타입은 값을 할당하거나 조건문 등에 의해 "타입이 진화(변경)"할 수 있습니다. 이는 타입 좁히기와 다릅니다.

타입 좁히기는 서브 타입으로만 좁혀지는 반면 any 타입의 진화는 서브 타입으로 "타입 좁히기", 상위 타입으로 "타입 확장", 다른 타입으로 "변경"이 모두 가능합니다.

any가 진화하는 방식은 일반적인 변수가 추론되는 원리와 동일합니다. 즉, 할당받는 값에 따라 any의 타입은 어떤 타입으로든 변경이 가능합니다.

any 타입의 진화는 "noImplicitAny 옵션이 true"이며, "암시적으로 any"인 경우에만 일어납니다.


  1. 선언 시점 variable 변수 타입은 any로 추론됩니다.

  2. variable 변수에 'a' 할당한 이후에는 variable 타입이 string 타입으로 진화합니다(타입 좁히기).

  3. variable 변수에 1을 할당한 이후에는 variable 타입이 number 타입으로 진화합니다(타입 변경).

  4. 선언 시점 result 변수의 타입은 any[] 타입으로 추론됩니다.

  5. push 메서드로 'a' 값을 요소로 추가한 이후에는 result 변수의 타입이 string[] 타입으로 진화합니다(타입 좁히기).

  6. push 메서드로 1을 요소로 추가한 이후에는 (string | number) [] 타입으로 진화합니다(타입 확장).

any를 진화시키는 방식보다 명시적 타입 구문을 사용하는 것이 안전한 타입을 유지하는 방법입니다.

참고로 타입의 진화는 값을 할당하거나 요소를 넣은 후에만 일어나기 때문에 할당이 일어난 줄의 타입을 조사하여도 여전히 any 또는 any[]로 표시됩니다.

unknown

unknown 타입은 사용자가 어떤 타입인지 알 수 없을 때, 어떤 타입인지 모르는 타입을 의미합니다.

포함 관계 : "unkwown", any > 모든 타입

Any Type vs Unknown Type

unknown 타입은 any 타입과 유사한 것처럼 보이지만 차이점이 분명합니다.

any 타입의 경우 모든 타입의 상위 타입이며 서브 타입이 될 수 있습니다. 하지만 unknown 타입의 경우에는 모든 타입의 상위 타입이지만 "서브 타입은 될 수 없습니다".

즉, unknown 타입의 값을 다른 타입에 할당하는 것은 불가능합니다.


userInput 변수의 타입이 unknown으로 지정했기 때문에 어떤 값이든 전달받을 수 있습니다.

unknown과 any가 서로 유사한 타입처럼 보이지만 unknown은 any 보다 좀 더 "제한적"입니다.

unknown을 사용하는 경우 반드시 "타입 단언문"이나 "타입 체크"를 강제하여 사용해야 합니다. 이는 반드시 특정 타입으로 제한하여 특정 타입을 의미하도록 만들어 사용해야 합니다.

위 코드처럼 userInput 변수의 타입을 unknown으로 지정한 이후 if 문을 통해 userInput 타입을 하나의 타입으로 특정되도록 할 수 있습니다.

never

never 타입은 값의 집합 관계에서 "공집합"을 의미하는 타입입니다. 즉, 어떤 값도 해당되지 않는 타입을 의미합니다.

never 타입은 함수에서 반환값 타입에 주로 사용되며 반환값을 아예 생성하지 않는 경우 반환값의 타입으로 지정할 수 있습니다. 즉, 함수의 끝에 절대 도달하지 않는다는 의미를 지닌 타입입니다.

예를 들어, 반드시 에러를 던지거나 무한 루프를 도는 함수의 경우 그 반환값으로 never 타입을 작성할 수 있습니다.

generatorError 함수는 에러가 발생하여 함수가 정상적으로 완료되지 않기 때문에 아무것도 반환하지 않습니다. 이외에도 무한 루프되는 함수의 경우에도 값을 반환할 수가 없기 때문에 never 타입을 지정할 수 있습니다.

void

함수의 반환값으로 주로 사용하는 타입이며 "반환 값을 설정할 수 없을 때" 사용하는 타입입니다.

포함 관계상 unkwown, any를 상위 집합으로 가지며, 하위 집합으로는 undefined 타입을 갖습니다.

만약 함수가 명시적으로 반환값을 갖지 않는 경우 함수 반환값이 void 타입으로 추론됩니다.

포함 관계 : unkown, any > "void" > undefined > any

null

null타입은 일반적으로 값이 없음을 나타낼 때 사용됩니다.

타입 포함 관계상 상위 타입은 unkwown, any 타입을 가지며, 하위 타입은 갖지 않습니다(strictNullChecks 옵션 true인 경우).

참고로 null 타입의 하위 타입은 없습니다. 즉, null 타입은 null 값만을 가질 수 있습니다.

포함 관계 : unkwown, any > "null" > any (strictNullChecks 옵션이 true인 경우)

undefinded

포함 관계상 unkwown, any, void를 상위 타입으로 가지며, 하위 타입은 갖지 않습니다. 즉, undefined 타입은 값으로 undefined만을 가질 수 있습니다(strictNullChecks 옵션이 true인 경우).

포함 관계 : unkwown, any > void > "undefined" > any (strictNullChecks 옵션이 true인 경우)

Literal type

리터럴 타입은 특정 값을 의미하는 타입입니다.

리터럴 타입은 유닛 타입으로 가장 작은 단위를 의미하며, 해당 리터럴 값만을 할당받을 수 있습니다.

포함 관계 : unkwon, any > Primitive type > "literal type" > any

value 변수의 타입을 string 으로 지정했습니다. 이는 string 타입보다 더 구체적인 타입으로 'string'이라는 문자열 값만을 가질 수 있습니다. 이를 리터럴 타입이라고 합니다.

profile
Frontend Dev

0개의 댓글