안녕하세요 😉
유유자적한 개발자 유로띠 입니다 😀
👏👏👏👏
TypeScript에 대해서 알아보겠습니다.
이번 포스팅은 타입스크립트 공식 핸드북의 원본과 해석본을
읽고 정리하는 글입니다.
TypeScript Handbook
TypeScript Handbook 정리 - The Basics
TypeScript - Everyday Types
TypeScript - Narrowing
TypeScript - More on Functions
TypeScript - Object Types
TypeScript - Type Manipulation
TypeScript - Classes
TypeScript - Modules
TypeScript - tsConfig
✅ Everyday Types
에 대해서 알아보겠습니다
🎉 TypeScript
Everyday Types
🟢 The primitives (원시 타입): string, number, boolean
string(문자열)
문자열을 나타냅니다.
let apple: string = 'Apple';
let day1: string = 'Day1';
number(숫자)
모든 부동 소수점 값을 사용할 수 있습니다.
let integer: number = 1207;
let float: number = 3.141592;
Boolean(불린)
단순한 참(true) / 거짓(false) 값을 나타냅니다.
let isUsed: boolean = true;
🟢 Arrays(배열)
순차적으로 값을 가지는 배열을 나타내며, 두 가지 방법으로 선언이 가능합니다.
let language: string[] = ['node', 'kotlin', 'go'];
let language: Array<string> = ['node', 'kotlin', 'go'];
🟢 any(모든 타입)
어떤 타입의 값도 할당할 수 있습니다.
다양한 값을 포함하는 배열을 나타낼 수도 있습니다.
let obj: any = { x: 0 };
obj.bar = 100;
obj = 'day1';
obj = {};
obj = null;
const list: any[] = [1, true, 'day1'];
🟢 unknown (알 수 없는 타입)
최상위 타입인 unknown은 any와 같이 어떤 타입의 값도 할당할 수 있지만, 다른 타입에는 할당할 수없습니다.
다만, 타입을 단언하면 할당할 수 있습니다.
let obj: unknown = '언노운';
let str: string = obj; //'unknown' 형식은 'string' 형식에 할당할 수 없습니다.ts(2322)
let str: string = obj as string;
🟢 Type Annotations on Variables
const, var, let과 같은 선언자로 변수를 선언할 때, 변수의 타입을 특정하기 위해 선택적으로 타입 선언을 합니다.
let name: string = 'yu';
String name = 'yu'; (x)
다만, 타입 스크립트는 자동으로 타입을 추론하여 타입을 적용합니다.
숫자 1207을 할당하여 number 타입으로 추론되었고, 이후 'type'이라는 string 타입의 값은 할당할 수 없기에 에러가 발생됩니다.
let num = 1207;
num = "type"; //'string' 형식은 'number' 형식에 할당할 수 없습니다.ts(2322)
타입스크립트가 타입을 추론하는 경우는 다음과 같습니다.
🔷 초기화된 변수
🔷 기본값이 설정된 매개 변수
🔷 변환 값이 있는 함수
#코틀린 variables와 비슷한 점
val a: Int = 1 // immediate assignment
val b = 2 // `Int` type is inferred
val c: Int // Type required when no initializer is provided
c = 3 // deferred assignment
🟢 Functions (함수)
Parameter Type Annotations (매개변수의 타입)
함수를 선언할 때, 매개변수의 뒤에 타입을 선언할 수 있습니다.
이를 통해 어떤 타입을 받을 수 있는지 알 수가 있습니다.
function greet(name: string) {
console.log('Hello, ' + name);
}
Return Type Annotations (반환 타입)
반환 값의 타입을 선언할 수 있습니다.
function greet(name: string): string {
return "Hello, " + name;
}
Anonymous Functions
다른 함수 선언과 다르게 타입스크립트는 문맥상으로 타입을 결정합니다.
names이란 배열은 string으로 선언하지 않았지만, 문맥상 string 타입이기 때문에 s 매개변수가 타입이 선언되지 않았음에도 s의 타입을
추론하여 string으로 판단하여서 toUpperCase()를 사용해도 에러가 발생되지 않습니다.
const names = ["Alice", "Bob", "Eve"];
names.forEach(function (s) {
console.log(s.toUpperCase());
});
이러한 프로세스를 contextual typing(문맥상의 타이핑)이라고 합니다.
🟢 Object Types
원시 타입(primitives: number, string, boolean, bigint, null, undefined)이 아닌 타입을 나타냅니다.
let obj: object = {};
let obj: object = [];
let obj: object = null;
여러 타입의 상위 타입이기 때문에 유용하지 않고 보다 정확한 타입 지정을 하기 위해 객체 속성들에 대한 타입을 개별적으로
지정할 수 있습니다.
Optional Properties
프로퍼티의 일부 또는 전부를 옵셔널로 특정할 수 있습니다.
속성에 ?를 추가하면 선택적 속성으로 정의할 수 있습니다.
function add(obj: { x: number; y?: number }): number {
return obj.x + (obj.y ?? 0);
}
add({ x: 2 });
add({ x: 2, y: 10 });
자바스크립트에서 존재하지 않은 프로퍼티에 접근하면, 런타임 에러 대신 undefined라는 값을 반환합니다.
때문에 옵셔널 프로퍼티로부터 값을 읽을 때, undefined를 체크하여 없는 경우 0을 리턴하도록 하였습니다.
위 예제는 다음 예제와 동일합니다.
function add(obj: { x: number; y: number | undefined }): number {
return obj.x + (obj.y || 0);
}
add({ x: 2, y: undefined });
add({ x: 2, y: 10 });
🟢 Union Types
2개 이상의 타입을 허용하는 경우 유니언이라고 합니다.
| (vertical bar)를 통해 타입을 구분합니다.
let union: (string | number);
union = 'Hello type!';
union = 123;
union = false; //'boolean' 형식은 'string | number' 형식에 할당할 수 없습니다.ts(2322)
모든 타입이 동작하는 작업만을 실행하도록 허락합니다.
다음 예제처럼 number 타입에서 동작하지 않는 toUpperCase는 사용할 수 없습니다.
let union: (string | number);
union = 'Hello type!';
union = 123;
union.toUpperCase(); //'number' 형식에 'toUpperCase' 속성이 없습니다.ts(2339)
string 타입에만 toUpperCase를 사용하려면 어떻게 해야 할까요?
방법은 타입을 코드로 좁히는 Narrowing 방법을 진행합니다.
function narrowing(union: string | number) {
if (typeof union === "string") {
return union.toUpperCase();
} else {
return union;
}
}
narrowing("apple");
narrowing(12);
🟢 Type Aliases (타입 별칭)
특정 타입에 대해 이름을 지정할 수 있는 기능입니다.
type 키워드를 사용해 새로운 타입 조합을 만들 수 있습니다.
type scoreType = number;
type resultType = number | string;
function printScore(score: number): scoreType {
return score;
}
printScore(11);
🟢 Interfaces
인터페이스는 타입스크립트 여러 객체를 정의하는 일종의 규칙이며 구조입니다.
Iemployee에서I는 Interface를 의미합니다.
속성에 ?를 사용하면 선택적 속성으로 정의할 수 있습니다.
interface Iemployee {
name: string;
id: number;
isAdult?: boolean; //Optional property
}
let employee: Iemployee = {
name: "Neo",
id: 123,
isAdult: true,
};
let employee2: Iemployee = {
name: "Neo",
id: 123,
};
읽기 전용 속성 (Readonly properties)
readonly 키워드를 사용하면 초기화된 값을 유지해야 하는 읽기 전용 속성을 정의할 수 있습니다.
interface Iemployee {
name: string;
readonly id: number;
isAdult?: boolean; //Optional property
}
let employee: Iemployee = {
name: "Neo",
id: 123,
isAdult: true,
};
employee.id = 456; //읽기 전용 속성이므로 'id'에 할당할 수 없습니다.ts(2540)
employee.name = "spring";
모든 속성이 readonly일 경우 유틸리티 타입을 활용할 수 있습니다.
interface Iemployee {
name: string;
id: number;
}
//Utility
let employee: Readonly<Iemployee> = {
name: "Neo",
id: 123,
};
employee.id = 456; //읽기 전용 속성이므로 'id'에 할당할 수 없습니다.ts(2540)
employee.name = "spring"; //읽기 전용 속성이므로 'name'에 할당할 수 없습니다.ts(2540)
🟢 Type Assertions (타입 단언)
타입 단언은 타입스트립트가 타입 추론을 통해 판단할 수 있는 타입의 범주를 넘는 경우, 더 이상 추론하지 않도록 지시할 수 있습니다.
즉, 타입스트립트는 모르지만 우리가 타입에 대한 정보를 알고 있는 상황을 의미합니다.
isNumber는 boolean이며, 숫자 여부를 판단하는 값임을 우리는 추론할 수 있습니다.
하지만 타입스크립트는 isNumber만으로는 추론할 수 없기 때문에 에러가 발생됩니다.
function assertionFunc(val: string | number, isNumber: boolean) {
//... logics
if (isNumber) {
val.toFixed(2);
//'string | number' 형식에 'toFixed' 속성이 없습니다.
//'string' 형식에 'toFixed' 속성이 없습니다.ts(2339)
}
}
두가지 방식으로 타입 선언을 할 수 있습니다.
변수 as 타입 (as - )
<타입>변수 (angle-bracket)
다만, 꺽쇠 괄호(<>)를 이용하면 JSX를 사용하는 경우 특정 구문 파싱에서 문제가 발생할 수 있으며, .tsx 파일에서는 동작하지 않습니다.
function assertionFunc(val: string | number, isnumber: boolean) {
//... logics
if (isnumber) {
(val as number).toFixed(2);
}
if (isnumber) {
(<number>val).toFixed(2);
}
}
또한, 타입 단언은 불가능한 강제 타입 변환을 방지합니다.
🟢 Literal Types
리터럴 타입은 집합 타입보다 구체적인 하위 타입입니다.
타입스크립트는 문자열과 숫자 두 가지 리터럴 타입이 있으며, 문자열이나 숫자에 정확한 값을 지정할 수 있습니다.
changingString은 변경될 수 있으므로 컴파일러는 문자열이라고 선언할 것입니다.
constantString은 문자열이 아닌 "Hello world"로 타입을 정합니다.
let changingString = 'Hello World';
changingString = 'Ola Mundo';
changingString;/* let changingString: string */
const constantString = 'Hello World';
constantString;/* const constantString: 'Hello World' */
리터럴 타입과 유니온 타입을 조합하면 유용하게 사용할 수 있습니다.
허용된 문자열이 아닌 다른 문자열을 사용하게 되면 오류가 발생됩니다.
type Alignment = "left" | "right" | "center";
function printText(s: string, alignment: Alignment) {
console.log(`string is ${s}.`);
console.log(`alignment is ${alignment}.`);
}
printText("Hello, world", "left");
printText("Good day, mate", "centre");
//'"centre"' 형식의 인수는 'Alignment' 형식의 매개 변수에 할당될 수 없습니다.ts(2345)
숫자 타입뿐만 아니라 다른 타입들과 조합하여 사용 가능합니다.
interface Options {
width: number;
}
function configure(x: Options | "auto") {
console.log(x);
}
configure({ width: 100 });
configure("auto");
configure("automatic");
//'"automatic"' 형식의 인수는 'Options | "auto"' 형식의 매개 변수에 할당될 수 없습니다.ts(2345)
🟢 null and undefined
값이 비어있거나, 초기화되지 않는 값을 표현하는 원시 값을 의미하며, 각각 자신의 타입 이름으로 사용할 수 있습니다.
let u: undefined = undefined;
let n: null = null;
strictNullChecks의 옵션에 따라 null과 undefined는 오직 any와 각자 자신들 타입에만 할당 가능합니다.
{
"compilerOptions": {
"strict": true,
"strictNullChecks": true // true or false
},
"exclude": ["node_modules"]
}
다만, "strictNullChecks": true이어도 void에는 undefined를 할당할 수 있습니다.
let vo: void = undefined;
//'undefined' 형식은 'number' 형식에 할당할 수 없습니다.ts(2322)
let num: number = undefined;
//'null' 형식은 'string' 형식에 할당할 수 없습니다.ts(2322)
let str: string = null;
🟢 Non-null Assertion Operator: Postfix ! (Non-null 단언 연산자)
!를 어떤 표현식 뒤에 사용하며, 피연산자가 null, undefined값이 아님을 단언할 수 있습니다.
null에 대한 처리는 다양하게 해결할 수 있지만, Non-null 단언 연산자를 이용해 간단하게 정리할 수 있습니다.
function liveDangerously(x?: number | null | undefined) {
//if statement
if (x) {
console.log(x.toFixed(2));
}
// Type assertion
console.log((x as number).toFixed(2));
// Non-null assertion operator
console.log(x!.toFixed(2));
}
🟢 Enums (열거형)
enums은 타입스크립트가 자바스크립트에 추가한 기능입니다.
숫자 혹은 문자열 값 집합에 이름을 부여할 수 있는 타입으로 일정한 범위로 정해져 있는 값이 있는 경우 활용할 수 있습니다.
기본적으로 값은 0부터 시작하여 1씩 증가합니다.
enum color {
Red, //(enum member) color.Red = 0
Blue,
Green,
Yellow,
Black,
White, //(enum member) color.White = 5
}
수동으로 값을 변경 가능하고 변경된 값 이후부터 다시 1씩 증가합니다.
enum color {
Red, //(enum member) color.Red = 0
Blue = 22,
Green, //(enum member) color.Green = 23
Yellow,
Black,
White, //(enum member) color.White = 26
}
Enum 타입의 특징 중 하나는 역방향 매핑을 지원합니다.
즉, 열거된 멤버에 대해 값으로 접근 또는 멤버로 값을 접근할 수가 있습니다.
enum color {
Red, //(enum member) color.Red = 0
Blue = 22,
Green, //(enum member) color.Green = 23
Yellow,
Black,
White, //(enum member) color.White = 26
}
console.log(color.Red); // 0
console.log(color[26]); // 'white'
console.log(color);
/*
{
'0': 'Red',
'22': 'Blue',
'23': 'Green',
'24': 'Yellow',
'25': 'Black',
'26': 'White',
Red: 0,
Blue: 22,
Green: 23,
Yellow: 24,
Black: 25,
White: 26
}
*/
🟢 Symbol (심벌)
자바스크립트는 5개의 원시 타입 : String, Number, Boolean, null, undefined을 가지고 있습니다.
Symbol은 ES6에서 새롭게 추가된 6번째 원시 타입으로 변경 불가능한 값입니다.
symbol 함수에 문자열 인자를 전달할 수 있으며, 생성에 어떠한 영향을 주지 않고 디버깅 용도로만 사용됩니다.
let firstSymbol = Symbol("first");
let secondSymbol = Symbol();
console.log(firstSymbol === secondSymbol); //false
console.log(firstSymbol === Symbol("first")); //false
symbol은 객체의 key로 사용될 수 있습니다.
한 가지 특이한 점은 symbol로 생성된 key에는 Object.keys, for in과 같은 메서드에서 접근하지 않습니다.
symbol과 함께 추가된 getOwnPropertySymbols()를 통해서 key에 접근할 수 있습니다.
const k2 = Symbol("k2");
const k3 = Symbol("k3");
const obj = {
k1: "key1",
[k2]: "key2",
[k3]: "key3",
};
console.log(Object.keys(obj)); // [ 'k1' ]
console.log(Object.getOwnPropertySymbols(obj)[0] === k2); // true
내장된 Symbol은 Well-known Symbols에 정의되어 있습니다.
참고
'Programming > TypeScript' 카테고리의 다른 글
TypeScript Handbook 정리1 - The Basics (0) | 2021.09.13 |
---|