— Typescript, devBooks — 3 min read
보리스 체르니의 [ 타입스크립트 프로그래밍 ] O'Reilly Media의 원서의 번역본을 기초로, 이해한 내용을 편하게 작성했습니다.
기본 타입(원시형 값의 타입)은 타입스크립트의 타입추론에게 맡기는 것이 좋다. 이때 선언 키워드( let, const )를 const로 한다면, 가장 좁게, 타입 리터럴로 추론한다. 이러한 특성을 활용하여 type assertion
이 가능하다. 후에 살펴보자.
참조형 값 - Object, Array - 은 const로 선언해도 타입을 더 좁게 추론하지 않고 let의 경우처럼 일반적인 추론이 적용된다.
enum 은 안전하게 사용하기 까다롭다.
타입스크립트의 배열과 튜플은 멋지다.
타입은 값과 그 값으로 할 수 있는 일의 집합을 말한다.
어떤 타입 T
에 대해 타입스크립트의 타입체커(typechecker
)는 타입을 확인하여, 유효하지 않은 동작이 실행되는 것을 막는다.
예를 들면,
Boolean
타입은 모든 불(true
or false
)와 불 값으로 수행하는 연산(&&, ||, ! 등)의 집합이다..toFixed
, .toPrecision
, .toString
등) 의 집합이다..concat
,.toUppercase
등) 의 집합이다.아래 예시에서, Type Annotation을 통해 타입스크립트에게 타입을 명시해주는 것을 보여 준다.
1// typescript2function squareOf(n: number){3 return n * n;4}5squareOf(2) // 46squareOf('x') // // Argument of type 'string' is not assignable to parameter of type 'number'.ts(2345)
1// javascript2function squareOf(n){3 return n * n4}5squareOf(2) // 46squareOf('x') // NaN
n
의 타입에 경계 로서, number을 명시하여, 다른 타입의 값이 오지 못하게 하는 것이다. 일단 타입을 제한하면, 타입스크립트(컴파일러)는 함수를 호출 하는 시점에서 호환되는 인수로 호출되는 지 확인하여 에러가 있다면, 에러가 발생한다.
any
최후의 보루. 가급적 사용하지 않는 것이 좋다. 말 그대로 any , 즉, 모든 값과 모든 작업의 집합이기 때문에, type checker는 작동하지 않는다. typescript의 강력한 기능을 포기하려는 게 아니라면, 사용을 자제하자.
any는 명시적으로 선언하지 않았는데, any로 추론되는 경우1라면 typescript가 컴파일 에러를 일으킬 것이다2.
1any로 추론되는 경우: 매개변수 타입 정의를 하지 않은 경우, 타입을 사용하지 않는 모듈을 임포트 한 경우 등.2any를 명시적으로 선언하여, 개발자의 의도를 밝혀서, 에러를 없앨 수 (는) 있다.TSC flag: noImplicitAny
타입스크립트의 default 설정은 any로 추론되는 값을 허용한다. TSC에 대한 글에서 정리한 바와 같이, 이 플래그는 strict 패밀리에 속하므로, tsconfig.json에서 strict을 활성화하면, 함께 활성화된다.
unknown
이 부분에서 보리스 체르니의 비유가 흥미롭다. - 영화 Point Break 에서 은행강도 갱에 잠입한 FBI 요원으로 비유한다.
unknown은 any와 마찬가지로 모든 값을 대표하지만, 타입스크립트가 unknown 값에 대해 타입을 검사하여 정제하기 전 까지는 타입스크립트가 해당 값을 사용할 수 없게 강제한다.
아래 예시를 통해 unknown
의 동작과 사용법을 들여다 보자.
1let a : unknown = 30 // unknown2// 타입스크립트가 어떤 타입을 unknown 타입으로 추론하는 경우는 없다.3let b = a === 120 // boolean4// unknown 타입과 boolean 타입의 값을 비교할 수 있다.5let c = a + 10 6// unknown 타입의 값을 특정 타입이라고 가정하고 사용할 수 없다.7if(typeof a === 'number'){8 let d = a + 10 9}10// 문맥을 통해 타입스크립트에게 해당 값의 타입을 증명해야 한다. 11// 타입스크립트는 컴파일 타임에 typeof를 활용하여 타입을 확인하기 때문에 위와 같은 타입정제가 가능하다.
boolean
아래 예시를 통해 boolean
의 동작과 사용법을 들여다 보자.
1let a = true // boolean2const a = true // true3// typescript가 a의 타입을 추론하게 한다. - const로 선언한다면, 가장 좁은 타입으로 추론되기 때문에, 타입 리터럴 true 타입으로 추론(강제)된다.
* 타입 리터럴은 타입 안정성Type Safety을 높여주며 타입스크립트의 차별되는 특성이다. 자바 같은 언어를 사용하는 친구에게 자랑할 만 하다 (!!!? 🤩)
number
bigint
string
symbol
Symbol.iterator
)를 설정하거나 객체가 어떤 인스턴스인지(Symbol.hasInstance
) 런타임에 오버라이딩하는 것과 비슷한 기능을 제공한다.object
객체 타입은 객체의 형태를 정의한다. 객체 타입만으로는, 객체 리터럴({}
)과 new
키워드를 통해 만든 객체를 구분할 수 없다. 이는 자바스크립트가 구조 기반 타입 (structural type)을 갖게 설계되었기 때문이다. 따라서 타입스크립트도 이름 기반의 타입(nominal type) 스타일보다는 자바스크립트 스타일을 선호한다.
구조기반 타입화 Structural Typing
구조 기반의 타입화 방식에서는 객체의 이름에 상관없이 객체가 어떤 프로퍼티를 갖는 지를 따진다 - 이름 기반 타입화에서는 이름에 따른다. 일부 언어에서는 이를 덕 타이핑이라 한다. 간단히 말해 구조만 같다면 호환되는 특성을 말한다.
객체를 타입과 함께 서술해보자!
1let a : object = {2 b: 'x'3} // Property 'b' does not exist on type 'object'.ts(2339)
예상과는 다른 결과다! object
는 any
보다 약간 더 좁은 타입으로, 서술하는 값에 대해 정보를 알려주지 않으며, 값 자체가 자바스크립 객체라고 ( null 이 아니라고 ) 말해줄 뿐이다.
=> 타입스크립트의 추론에 맡겨보자!
1let a1 = {2 b: 'x'3}; 4a1.b // string5let a2 = {6 c: {7 b: 'x'8 }9}10a2.c // (property) c: {11 b: string;12}
객체 리터럴 문법에서는 선언된 객체의 구조를 그대로 추론한다! 식별자 a2
의 경우 처럼, 객체 안의 객체 구조도 제대로 추론한다.
타입스크립트에서 객체를
const
를 선언할 때 타입 추론은 기본 타입의 경우와 다르게 여전히let
키워드로 선언할 때와 같은 추론결과를 내놓는다.
객체 리터럴은 프로퍼티를 명시적으로 구조를 제한(선언)하는 것과 같다
1let c= {2 firstName: "john",3 lastName: "McCoy",4};56class Person {7 constructor(8 public firstName: string = "richard",9 public lastName: string = "Lim"10 ) {}11}1213c = new Person("Kim"); // works!
위 예시에서 c
의 타입은 Person
클래스 인스턴스와 호환되므로 - 만족하므로 타입스크립트는 Person
인스턴스를 c
에 할당하는 동작을 허용한다.
기본적으로 타입스크립트는 객체 프로퍼티에 엄격한 편이다. 사전에 정의된 타입(구조)를 벗어나면 에러를 발생한다.
선택형 프로퍼티 선언과 인덱스 시그니쳐
1// optional property2let a : {3 b: number,4 c?: string,5 [key: number]: boolean6}7// index signature8let cinemaSeatingAssignment: {9 [seat: string]: string10} = {11 '34D': 'Danny DW Kim',12 '34E': 'Dan Abramov'13}
인덱스 시그니쳐
[key: T]: U
같은 문법을 인덱스 시그니쳐라 한다. 즉 '이 객체에서 모든 T타입의 키는 U타입의 값을 갖는다'라고 읽을 수 있다. 자바스크립트 객체의 프로퍼티 키는 문자열 또는 (배열 객체의 경우) 숫자를 키의 타입으로 사용하기 때문에, T타입은number
또는string
타입에 할당할 수 있는 타입이어야 한다.
readonly 한정자를 활용한 읽기전용 필드 정의
1let user : {2 readonly firstName: string3} = firstName: 'anne'4user.firstName = 'anne with e' // Cannot assign to 'firstName' because it is a read-only property.ts(2540)
저자 보리스 체르니의 객체 요약
객체 리터럴 또는 형태라 불리는 표기법 {a: string}
은 객체가 어떤 필드를 포함할지 알거나 객체의 모든 값이 같은 팁을 가질 때 사용할 수 있다.
빈 객체 리터럴 표기법 {}
. 이건 추천하지 않는다 - 빈 객체 타입에는 null과 undefined를 제외한 모든 타입을 할당 할 수 있다.
1let danger: {};2danger = 3; // 여기서 에러없이 동작한다..! Type Safety를 생각한다면 쓰지말자
object
타입. 필드에 관심없고, 그저 객체가 필요할 때나 사용한다.
Object
타입. 사용하지 않는 것이 좋다.