조금 복잡한 타입에 대해서 다루는 글이며 내용이 길어지기 때문에 파트별로 나눠서 게시할 것이다.
타입 별칭(type alias)을 이용하여 이미 존재하는 타입에 별칭을 붙여 복잡한 타입을 간단하게 쓸 수 있게 도와준다.
또한, 프로그래머의 의도를 보다 명확하게 나타낼 수 있다.
type NewType = Type;
별칭을 갖게 될 타입의 자리(Type
)엔 기본 타입을 포함한 모든 타입이 올 수 있다.
type UUID = string;
type Height = number;
type AnotherUUID = UUID;
type Animals = Animal[];
type User = {
name: string;
height: number;
};
이 때 별칭은 이름만 재정의 하는 것이지 실제로 새로운 타입이 생성되는 것은 아니라는 점에 유의해야 한다.
예시로 아래와 같은 코드의 에러메시지에는 UUID
타입이 아닌 string
타입으로 에러메시지를 띄워준다.
type UUID = string;
function getUser(uuid: UUID) {
/* ... */
}
// error TS2345: Argument of type '7' is not assignable to parameter of type 'string'.
getUser(7);
함수의 타입을 결정하기 위해서는 아래의 두 가지 정보가 필요하다.
변수의 타입을 표기할 때와 마찬가지로 매개변수 뒤에 콜론(:
)을 붙이고 타입을 적는다.
(param1: number, param2: string)
반환값의 타입은 매개변수 목록을 닫는 괄호()
)와 함수 본문을 여는 여는 대괄호({
)사이에 콜론(:
)을 붙이고 표기한다.
function (): number { ... }
일반적으로 반환값이 있는 형태는 아래와 같다.
function sum(a: number, b: number): number {
return (a + b);
}
반환 값이 없는 경우 반환 타입으로 void
를 사용한다.
function logGreetings(name: string): void {
console.log(`Hello, ${name}!`);
}
void
반환 타입을 갖는 함수가 undefined
나 null
이외의 값을 반환하면 타입 에러다.
void
가 아닌 반환 타입을 갖는 함수가 아무 값도 반환하지 않아도 타입 에러다.
function notReallyVoid(): void {
return 1;
}
// error TS2322: Type '1' is not assignable to type 'void'.
function actuallyVoid(): number { }
// error TS2355: A function whose declared type is neither 'void' nor 'any' must return a value.
함수 표현식을 사용할 경우 함수 타입을 지정할 수 있다.
함수 타입의 값에 타입 표기를 붙이기 위해서는 화살표 함수 정의 문법과 비슷한 문법을 사용한다.
(...params) => type
() => type
예시는 아래와 같다.
const yetAnotherSum: (a: number, b: number) => number = sum;
const onePlusOne: () => number = () => 2;
const arrowSum: (a: number, b: number) => number = (a, b) => (a + b);
타입 별칭도 사용 가능하다.
type SumFunction = (a: number, b: number) => number;
const definitelySum: SumFunction = (a, b) => (a + b);
TS에서도 JS의 기본 매개변수 문법을 사용할 수 있다.
매개변수명: 타입 = 기본값
예시는 아래와 같다.
function greetings(name: string = 'stranger'): void {
console.log(`Hello, ${name}`);
}
greetings('Heejong'); // Hello, Heejong!
greetings(); // Hello, stranger!
객체타입을 지정할 때처럼 함수의 매개변수도 선택 매개변수 지정이 가능하다.
사용법은 객체의 선택속성처럼 물음표(?
)를 속성명 뒤에 붙여주면 된다.
function fetchVideo(url: string, subtitleLanguage?: string) {
const option = { url };
if (subtitleLanguage) {
option.subtitleLanguage = true;
}
/* ... */
}
fetchVideo('https://example.com', 'ko'); // okay
fetchVideo('https://example.com'); // also okay
이 때 주의할 점은 매개변수 정의 순서에서 선택 매개변수 이후에 필수 매개변수를 두면 에러다.
function invalidFetchVideo(subtitleUrl?: string, url: string) {
/* ... */
}
//error TS1016: A required parameter cannot follow an optional parameter.
JS에서 한 함수가 여러 쌍의 매개변수-반환 타입 쌍을 갖는 경우가 꽤나 흔하다.
function (value) {
if (typeof value === 'string') {
return value.replace(/./,'');
} else if (typeof value === 'number') {
return value * 2
}
}
이 때 이렇게 여러 쌍의 매개변수-반환 타입 쌍을 갖는 경우의 함수 타입을 정의할 수 있게 TS는 함수 오버로딩을 지원한다.
TS의 함수 오버로딩의 특징을 아래와 같다.
즉, 오버로딩을 통해 여러 형태의 함수 타입을 정의할 수는 있지만,
실제 구현은 한 번만 가능하기 때문에 타입에 대한 분기는 구현 시 함수 몸체에서 이뤄져야 한다.
// 함수 타입 시그니처
function double(str: string): string;
function double(num: number): number;
function double(arr: boolean[]): boolean[];
// 함수 구현
function double(arg) {
if (typeof arg === 'string') return `${arg}${arg}`;
else if (typeof arg === 'number') return arg * 2;
else if (Array.isArray(arg)) return arg.concat(arg);
}
// 함수 호출
// 오버로딩을 통해 정의된 `double`함수는 호출하는 인자의 타입에 따라 반환 타입이 달라진다.
const num = double(3); // number
const str = double('ab'); // string
const arr = double([true, false]); // boolean[]
JS를 공부했다면 this
는 동적으로 바인딩 된다는 것을 알고 있을 것이다.
this
의 동적 바인딩은 this
의 타입을 추론하는 것을 매우 어렵게 만든다.
TS에서는 이러한 어려움을 해결하기 위해 함수 내에서의 this
타입을 명시할 수 있는 수단을 제공한다.
함수의 this
타입을 명시하기 위해서는 타입 시그니쳐에서 매개변수 가장 앞에 this
를 추가해주면 된다.
이 때 this
타입은 타입 시스템을 위해서만 존재하는 일종의 가짜 타입이다.
즉, this
매개변수를 추가해도 함수가 받는 인자 수와 실제 동작은 변하지 않는다.
interface HTMLElement {
tagName: string;
/* ... */
}
interface Handler {
(this: HTMLElement, event: Event, callback: () => void): void;
}
let cb: any;
// 실제 함수 매개변수에는 this가 나타나지 않음
const onClick: Handler = function(event, cb) {
// this는 HTMLElement 타입
console.log(this.tagName);
cb();
}
만약 this
에 접근하는 일 자체를 막으려면 this
의 타입을 void
로 명시하면 된다.
interface NoThis {
(this: void): void;
}
const noThis: NoThis = function() {
console.log(this.a); // Property 'a' does not exist on type 'void'.
}