💡 tl;dr
지원 타입 목록
타입 선언 (Type Declaration)
타입 추론 (Type Inference)
타입 단언 (Type Assertions)
타입 가드 (Type Guards)
지원 타입 목록
Typescript는 Javascript의 타입 외에도 고유의 타입이 추가로 제공된다.
Type
JS
TS
Description
boolean
✅
✅
true와 false
null
✅
✅
값이 없다는 것을 명시
undefined
✅
✅
값을 할당하지 않은 변수의 초기값
number
✅
✅
숫자(정수와 실수, Infinity, NaN)
string
✅
✅
문자열
symbol
✅
✅
고유하고 수정 불가능한 데이터 타입이며 주로 객체 프로퍼티들의 식별자로 사용(ES6에서 추가)
object
✅
✅
객체형(참조형)
array
✅
배열
tuple
✅
고정된 요소수 만큼의 타입을 미리 선언후 배열을 표현
enum
✅
열거형. 숫자값 집합에 이름을 지정한 것
any
✅
타입 추론(type inference)할 수 없거나 타입 체크가 필요없는 변수에 사용, var 키워드로 선언한 변수와 같이 어떤 타입의 값이라도 할당 가능
void
✅
일반적으로 함수에서 반환값이 없을 경우 사용
never
✅
결코 발생하지 않는 값
타입 선언 (Type Declaration)
타입스크립트는 일반 변수, 매개 변수(Parameter), 객체 속성(Property) 등에 : TYPE
과 같은 형태로 타입을 지정할 수 있다.
TypeScript
는 JavaScript
의 모든 데이터 타입을 허용한다.
원시 타입 (primitive type)
1 2 3 let foo_s: string = 'text' let foo_n: number = 0 ; let foo_b: boolean = true ;
이 때 만약 지정된 타입이 아닌 다른 타입의 데이터를 할당하려고 시도하면 에러를 발생한다.
1 2 3 4 5 6 let foo_s: string = 0 ; let foo_n: number = 1 ;foo_n = 'text' ;
배열 (Array)
배열은 다음과 같이 두 가지 방법으로 타입을 선언할 수 있다.
1 let list: number [] = [1 , 2 , 3 ];
1 let list: Array <number > = [1 , 2 , 3 ];
튜플 (Tuple)
튜플 타입을 사용하면, 요소의 타입과 개수가 고정된 배열을 표현할 수 있다.
요소들의 타입이 모두 같을 필요는 없다
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 let x: [string , number ];x = ["hello" , 10 ]; x = [10 , "hello" ]; console .log(x[0 ].substring(1 )); console .log(x[1 ].substring(1 )); x[3 ] = "world" ; console .log(x[5 ].toString());
열거형 (Enum)
열거형(enum
)은 숫자값 집합에 이름을 지정한 것이다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 enum Color1 {Red, Green, Blue};let c1: Color1 = Color1.Green;console .log(c1); enum Color2 {Red = 1 , Green, Blue};let c2: Color2 = Color2.Green;console .log(c2); enum Color3 {Red = 1 , Green = 2 , Blue = 4 };let c3: Color3 = Color3.Blue;console .log(c3);
모든 타입 (Any)
Any
는 모든 타입을 의미한다.
일반적인 자바스크립트 변수와 동일하게 어떤 타입의 값도 할당할 수 있다.
외부 자원을 활용해 개발할 때 불가피하게 타입을 단언할 수 없는 경우, 유용할 수 있다.
1 2 3 4 5 6 7 let any : any = 123 ;any = 'Hello world' ;any = {};any = null ;const list: anyArray[] = [1 , true , 'Anything!' ];
강한 타입 시스템의 장점을 유지하기 위해 Any
사용을 엄격하게 금지하려면, 컴파일 옵션 "noImplicitAny": true
를 통해 Any
사용 시 에러를 발생시킬 수 있다.
알 수 없는 타입 (Unknown)
Any
와 같이 최상위 타입인 Unknown
은 알 수 없는 타입을 의미한다.
Any
와 같이 Unknown
에는 어떤 타입의 값도 할당할 수 있지만, Unknown
을 다른 타입에는 할당할 수 없다.
일반적인 경우 Unknown
은 타입 단언(Assertions
)이나 타입 가드(Guards
)를 필요로 한다.
1 2 3 4 5 6 7 let a: any = 123 ;let u: unknown = 123 ;let v1: boolean = a; let v2: number = u; let v3: any = u; let v4: number = u as number ;
객체 (Object)
기본적으로 typeof
연산자가 "object"
로 반환하는 모든 타입
컴파일러 옵션에서 엄격한 타입 검사(strict
)를 true
로 설정하면, null
은 포함하지 않음.
object
타입을 쓰면 Object.create
같은 API 가 더 잘 나타난다.
1 2 3 4 5 6 7 8 9 declare function create (o: object | null ): void ;create({ prop : 0 }); create(null ); create(42 ); create("string" ); create(false ); create(undefined );
Void
void
는 어떤 타입도 존재할 수 없음을 나타내기 때문에, any
의 정반대로 작동한다.
void
는 보통 함수에서 반환 값이 없을 때 반환 타입을 표현하기 위해 쓰인다.
1 2 3 function warnUser ( ): void { console .log("This is my warning message" ); }
void
를 타입 변수를 선언하는 것은 유용하지 않은데, 그 변수에는 null
또는 undefined
만 할당할 수 있기 때문이다. ( --strictNullChecks
를 사용하지 않을 때만 해당 )
1 2 let unusable: void = undefined ;unusable = null ;
Null과 Undefined
기본적으로 Null과 Undefined는 모든 타입의 하위 타입으로, 다음과 같이 각 타입에 할당할 수 있다.
심지어 서로의 타입에도 할당 가능하다.
1 2 3 4 5 6 7 8 let num: number = undefined ;let str: string = null ;let obj: { a : 1 , b : false } = undefined ;let arr: any [] = null ;let und: undefined = null ;let nul: null = undefined ;let voi: void = null ;
컴파일 옵션 "strictNullChecks": true
를 사용하면 Null
과 Undefined
가 서로의 타입을 할당할 수 없다.
단, Void
에는 Undefined
을 할당할 수 있다.
1 let voi: void = undefined ;
Never
Never
는 절대 발생하지 않을 값을 나타내며, 어떠한 타입도 적용할 수 없다
1 2 3 function error (message: string ): never { throw new Error (message); }
보통 빈 타입 배열을 타입으로 선정할 경우 에러로 볼 수 있다.
1 2 3 const never : [] = [];never .push(3 );
유니언 (Union)
2개 이상의 타입을 허용하는 경우
|
(vertical bar)를 통해 타입을 구분
()
는 선택사항이다.
1 2 3 4 5 let union: (string | number );union = 'Hello type!' ; union = 123 ; union = false ;
인터섹션 (Intersection)
&
(ampersand)를 사용해 2개 이상의 타입을 조합하는 경우
새 타입을 생성하지 않고 기존 타입들을 조합할 수 있어 유용하나, 자주 사용되진 않음.
유니언이 Or
라면, 인터섹션은 And
에 해당
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 interface IUser { name : string , age : number } interface IValidation { isValid : boolean } const heropy: IUser = { name : 'Heropy' , age : 36 , isValid : true }; const neo: IUser & IValidation = { name : 'Neo' , age : 85 , isValid : true }; interface IUserNew { name : string , age : number , isValid : boolean } const evan: IUserNew = { name : 'Evan' , age : 36 , isValid : false };
함수 (Function)
화살표 함수를 이용해 타입을 지정할 수 있다.
인수의 타입과 반환 값의 타입을 입력한다.
1 2 3 4 5 6 7 8 9 10 11 let myFunc: (arg1: number , arg2: number ) => number ;myFunc = function (x, y ) { return x + y; }; let yourFunc: () => void ;yourFunc = function ( ) { console .log('Hello world~' ); };
타입 추론 (Type Inference)
명시적으로 타입 선언이 되어있지 않은 경우, 타입스크립트는 타입을 추론해 제공한다.
타입스크립트가 타입을 추론하는 경우
초기화된 변수
기본값이 설정된 매개 변수
반환 값이 있는 함수
1 2 3 4 5 6 7 8 let num = 12 ;function add (a: number , b: number = 2 ): number { return a + b; }
타입 추론은 엄격하지 않은 타입 선언( strict: false
등 )과는 다르다.
따라서 타입 추론을 활용하여 ‘모든 곳에 타입 명시’ 를 피하는 편이 코드 가독성 에는 더 좋다.
타입 단언 (Type Assertions)
개발자가 컴파일러보다 값의 유형을 더 잘 알고 있을 때 이를 명시하는 방법
컴파일러에게 내가 뭘 하고 있는지 알아
라고 말하는 것
다른 언어의 타입 변환(캐스팅)과 유사 하지만 데이터를 재구성하거나 검사하지 않음
캐스팅 : 이미 선언된 타입이 런타임에서 변하는 것
런타임에 영향을 미치지 않고, 온전히 컴파일러만 사용
타입 스크립트는 개발자가 필요한 어떤 검사를 수행했다고 인지
타입 단언에는 두 가지 형태가 있다. 하나는 “angle-bracket
“ 문법이다.
1 2 3 let someValue: any = "this is a string" ;let strLength: number = (<string >someValue).length;
다른 하나는 as -
문법이다.
1 2 3 let someValue: any = "this is a string" ;let strLength: number = (someValue as string ).length;
위 두 예제는 동일하므로 기호에 따라 선택하여 사용하다.
그러나 만약 TypeScript
를 JSX
와 함께 사용할 때는 as -
스타일의 문법만 허용된다.
타입 가드 (Type Guards)
타입을 매번 보장하기 위해 타입 단언을 여러 번 사용해야 하는 경우가 있다.
1 2 3 4 5 6 7 let pet = getSmallPet();if ((pet as Fish).swim) { (pet as Fish).swim(); } else if ((pet as Bird).fly) { (pet as Bird).fly(); }
타입 가드는 타입스크립트가 추론 가능한 특정 범위(scope
)에서 타입을 보장할 수 있다.
NAME is TYPE
형태의 타입 서술어 를 반환 타입으로 명시한 함수다.
여기서 NAME
은 반드시 현재 함수의 매개변수 이름이어야 한다.
1 2 3 function isFish (pet: Fish | Bird ): pet is Fish { return (pet as Fish).swim !== undefined ; }
pet is Fish
는 위 예제의 타입 서술어다.
이를 활용하여 앞선 예제는 아래와 같이 깔끔해진다
1 2 3 4 5 6 7 if (isFish(pet)) { pet.swim(); } else { pet.fly(); }
TypeScript 가 pet
이 if문 안에서 Fish
라는 것을 알고 있을뿐만 아니라, else문 안에서 Fish
가 없다는 것을 알고 있으므로, Bird
를 반드시 가지고 있어야한다.
위 방식뿐 아니라 제공 가능한 타입가드들이 더 있다.typeof
, in
, instanceof
연산자를 직접 사용하는 타입 가드다.
in 연산자 사용하기
in
연산자는 타입을 좁히는 작용을 한다.
n in x
에서, n
은 문자열 리터럴 혹은 문자열 리터럴 타입이고 x
는 유니언 타입이다.
“true
“ 분기에서는 선택적 혹은 필수 프로퍼티 n
을 가지는 타입으로 좁힌다.
“false
“ 분기에서는 선택적 혹은 누락된 프로퍼티 n을 가지는 타입으로 좁혀진다.
1 2 3 4 5 6 function move (pet: Fish | Bird ) { if ("swim" in pet) { return pet.swim(); } return pet.fly(); }
typeof 타입 가드
유니언 타입을 사용하는 버전의 padLeft
코드 예제다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 function isNumber (x: any ): x is number { return typeof x === "number" ; } function isString (x: any ): x is string { return typeof x === "string" ; } function padLeft (value: string , padding: string | number ) { if (isNumber(padding)) { return Array (padding + 1 ).join(" " ) + value; } if (isString(padding)) { return padding + value; } throw new Error (`Expected string or number, got '${padding} '.` ); }
그러나 타입이 원시 값인지 확인하는 함수를 정의하는 것은 너무나 귀찮은 일이다.
다행히도 TypeScript
는 typeof
를 타입 가드로 인식하기 때문에 typeof x === "number"
를 함수로 추상할 필요가 없다.
즉, 타입 검사를 인라인으로 작성할 수 있다.
1 2 3 4 5 6 7 8 9 function padLeft (value: string , padding: string | number ) { if (typeof padding === "number" ) { return Array (padding + 1 ).join(" " ) + value; } if (typeof padding === "string" ) { return padding + value; } throw new Error (`Expected string or number, got '${padding} '.` ); }
typeof
타입 가드는 두 가지 다른 형식인 typeof v === "typename"
와 typeof v !== "typename"
이 있다.
여기서 typename
은 “number
“, “string
“, “boolean
“, “symbol
“ 중 하나여야 한다.
TypeScript
에서 위에 없는 다른 문자열과 비교하는 것을 막지는 않지만, 타입 가드의 표현식으로 인지되지 않는다.
instanceof 타입 가드
instanceof
타입 가드는 생성자 함수를 사용하여 타입을 좁히는 방법이다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 interface Padder { getPaddingString(): string } class SpaceRepeatingPadder implements Padder { constructor (private numSpaces: number ) { } getPaddingString ( ) { return Array (this .numSpaces + 1 ).join(" " ); } } class StringPadder implements Padder { constructor (private value: string ) { } getPaddingString ( ) { return this .value; } } function getRandomPadder ( ) { return Math .random() < 0.5 ? new SpaceRepeatingPadder(4 ) : new StringPadder(" " ); } let padder: Padder = getRandomPadder();if (padder instanceof SpaceRepeatingPadder) { padder; } if (padder instanceof StringPadder) { padder; }
instanceof
의 오른쪽은 생성자 함수여야 하며, TypeScript
는 다음과 같이 타입을 좁힌다.
함수의 prototype
프로퍼티 타입이 any
가 아닌 경우
타입의 생성자 시그니처에서 반환된 유니언 타입일 경우
위 예제와 같은 순서대로 진행된다.
참고