Typscript Basics
2020. 07. 17
Jaewoo KIM
Microsoft 에서 개발하고 유지/관리하는 오픈 소스
일반 자바스크립트로 컴파일 되는 자바스크립트 상위호환
2012년 10월 처음 릴리즈
- 크로스 플랫폼 지원: JS 실행되는 모든 플랫폼 사용 가능
- 객체 지향 언어: 클래스, 인터페이스, 모듈
- 정적 타입: 컴파일 오류 체크
- ...
💁♂️ 기본 타입
-
Boolean
-
Number
-
String
-
Array
-
Tuple
-
Enum
-
Any
-
Void
-
Null and Undefined
-
Never
-
Object
Boolean, Number, String
// Boolean
let isTrue: boolean = true;
let isFalse: boolean = false;
// Number
let decimal: number = 6;
let hex: number = 0xf00d;
let binary: number = 0b1010;
let octal: number = 0o744;
// String
let fullName: string = `Jaewoo KIM`;
let age: number = 29;
let sentence: string = `Hello, my name is ${ fullName }.
Array, Tuple
// Array
let list: number[] = [1, 2, 3];
let list: Array<number> = [1, 2, 3];
// Tuple: 요소의 타입과 개수가 고정된 배열
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"; // 오류, '[string, number]' 타입에는 프로퍼티 '3'이 없습니다.
console.log(x[5].toString()); // '[string, number]' 타입에는 프로퍼티 '5'가 없습니다
Enum
enum Color {Red, Green, Blue}
let c: Color = Color.Green;
// 시작 번호 설정
enum Color {Red = 1, Green, Blue}
let c: Color = Color.Green;
enum Color {Red = 1, Green = 2, Blue = 4}
let c: Color = Color.Green;
console.log(c) // 2
console.log(Color[1]) // Red
console.log(Color[2]) // Green
console.log(Color[3]) // Blud
관련된 값의 집합에 더 나은 이름을 붙여줄 수 있다
Any
let notSure: any = 4;
notSure = "type이 자유룝다";
notSure = false;
// object와 차이
let notSure: any = 4;
notSure.ifItExists(); // 성공, 런타임에 존재할 것이다
notSure.toFixed(); // 성공, 존재란다
let prettySure: Object = 4;
prettySure.toFixed(); // 프로퍼티 존재하지 않음
let list: any[] = [1, true, "문자"];
list[1] = 100;
알지 못하는 타입을 표현할 때 (3rd party library)
Type assetions
// angle-bracket 사용
let someValue: any = "this is a string";
let strLength: number = (<string>someValue).length;
// as 사용
let someValue: any = "this is a string";
let strLength: number = (someValue as string).length;
컴파일러 대신 더 구체적인 타입을 명시하는 것
'as' 와 '<>' 취향 차이 동작은 동일함
Type Aliases
type MyType = string;
type YourType = string | number | boolean;
type TUser = {
name: string,
age: number,
isValid: boolean
} | [string, number, boolean];
let userA: TUser = {
name: 'Neo',
age: 85,
isValid: true
};
let userB: TUser = ['Evan', 36, false];
type 키워드를 사용해 새로운 타입 조합을 만들 수 있다
일반적인 경우 둘 이상의 조합으로 구성하기 위해 유니온을 많이 사용
💁♂️ 인터페이스
Interface
function printLabel(labeledObj: { label: string }) {
console.log(labeledObj.label);
}
let myObj1 = {label: "Size 10 Object"};
printLabel(myObj1);
let myObj2 = {size: 10, label: "Size 10 Object"};
printLabel(myObj2);
// Property 'label' is missing in type '{ size: number; }' but required in type '{ label: string; }'.(2345)
let myObj3 = {size: 10};
printLabel(myObj3);
클래스 또는 객체를 위한 타입을 지정 할 때 사용되는 문법
최소한 필요한 프로퍼티가 있는지 검사
Interface
interface LabeledValue {
label: string;
}
// 명시적 구현이 필요없음, 중요한건 형태
function printLabel(labeledObj: LabeledValue) {
console.log(labeledObj.label);
}
let myObj1 = {label: "Size 10 Object"};
printLabel(myObj1);
// 순서는 상관 없음
let myObj2 = {size: 10, label: "Size 10 Object"};
printLabel(myObj2);
클래스 또는 객체를 위한 타입을 지정 할 때 사용되는 문법
Interface (Optional Properties)
interface SquareConfig {
color?: string;
width?: number;
}
function createSquare(config: SquareConfig): {color: string; area: number} {
let newSquare = {color: "white", area: 100};
if (config.color) {
newSquare.color = config.color;
}
if (config.width) {
newSquare.area = config.width * config.width;
}
return newSquare;
}
let mySquare = createSquare({color: "black"});
객체 프로퍼티의 일부분만 사용하는 경우
Interface (Optional Properties)
interface SquareConfig {
color?: string;
width?: number;
}
function createSquare(config: SquareConfig): {color: string; area: number} {
let newSquare = {color: "white", area: 100};
if (config.clor) {
// Error: Property 'clor' does not exist on type 'SquareConfig'
newSquare.color = config.color;
}
if (config.width) {
newSquare.area = config.width * config.width;
}
return newSquare;
}
let mySquare = createSquare({color: "black"});
객체 프로퍼티의 일부분만 사용하는 경우
Interface (Readonly Properties)
interface Point {
readonly x: number;
readonly y: number;
}
let p1: Point = { x: 10, y: 20 };
p1.x = 5; // 오류!
객체가 생성 된 이후 수정이 불가능함
Interface (Readonly Properties)
let a: number[] = [1, 2, 3, 4];
let ro: ReadonlyArray<number> = a;
ro[0] = 12; // 오류!
ro.push(5); // 오류!
ro.length = 100; // 오류!
a = ro; // 오류!
모든 변경 메서드가 제거된 ReadonlyArray<T>
초과 프로퍼티 검사
interface SquareConfig {
color?: string;
width?: number;
}
function createSquare(config: SquareConfig): { color: string; area: number } {
// ...
}
// 오류 발생
let mySquare = createSquare({ colour: "red", width: 100 });
// 타입 단언을 통해 통과가능
let mySquare = createSquare({ width: 100, opacity: 0.5 } as SquareConfig);
초과 프로퍼티 검사
// 인덱스 서명
interface SquareConfig {
color?: string;
width?: number;
[propName: string]: any;
}
// 객체를 다른 변수에 할당하는 것도 방법
let squareOptions = { colour: "red", width: 100 };
let mySquare = createSquare(squareOptions);
let squareOptions = { colour: "red" };
let mySquare = createSquare(squareOptions);
문자열 인덱스 서명을 추가하는 것도 방법
초과 프로퍼티 검사 문제는 대부분 오류니 잘 확인해야함...
Interface (Fuction type)
interface SearchFunc {
(source: string, subString: string): boolean;
}
let mySearch: SearchFunc;
mySearch = function(source: string, subString: string) {
let result = source.search(subString);
return result > -1;
}
// 타입추론
let mySearch: SearchFunc;
mySearch = function(src, sub) {
let result = src.search(sub);
return result > -1;
}
프로퍼티로 객체를 기술하는 것 이외에도 함수 타입을 정의할 수 있다
💁♂️ 제네릭
Generic
// number에 한정적이다
function echo(arg: number): number {
console.log(arg)
return arg;
}
// 반환 타입에 대한 정보를 알 수 없다
function echo(arg: any): any {
console.log(arg)
return arg;
}
단일 타입이 아닌 다양한 타입에서 작동하는 컴포넌트를 작성가능하다
제네릭이 없다면 특정 타입을 명시해야 함
Generic
function echo<T>(arg: T): T {
return arg;
}
let output = echo<string>("myString");
// 타입추론
// 복잡한 경우 컴파일러가 유추 불가능, 명시 필요
let output = echo("myString");
단일 타입이 아닌 다양한 타입에서 작동하는 컴포넌트를 작성가능하다
컴파일러는 'myString' 을 보고 타입으로 T를 결정
Generic Types
function identity<T>(arg: T): T {
return arg;
}
let myIdentity1: <T>(arg: T) => T = identity;
// 다른 이름으로도 사용 가능
let myIdentity2: <K>(arg: K) => K = identity;
// 객체 리터럴 타입으로 선언 가능
let myIdentity3: { <T>(arg: T): T } = identity;
함수 자체 타입과 제네릭 인터페이스
Generic Types
interface GenericIdentityFn {
<T>(arg: T): T;
}
function identity<T>(arg: T): T {
return arg;
}
// 인터페이스로 지정
let myIdentity: GenericIdentityFn = identity;
함수 자체 타입과 제네릭 인터페이스
객체 리터럴을 인터페이스로 가져올 수 있음
Generic 타입 조건
extends 키워드를 사용하여 제약조건을 추가한다
interface MyType<T extends string | number> {
name: string,
value: T
}
const dataA: MyType<string> = {
name: 'Data A',
value: 'Hello world'
};
// TS2344: Type 'boolean' does not satisfy the constraint 'string | number'.
const dataB: MyType<boolen> = {
name: 'Data B',
value: true
};
💁♂️ Enums
Numeric enums
enum Direction {
Up = 1,
Down,
Left,
Right,
}
// 초기화 하지 않아도 됨
// 값이 중요하지 않고 단순히 구별돼야 하는 경우 유용
enum Direction {
Up,
Down,
Left,
Right,
}
값이 숫자로 이뤄짐
String enums
enum Direction {
Up = "UP",
Down = "DOWN",
Left = "LEFT",
Right = "RIGHT",
}
값이 문자로 이뤄짐
값이 중요하지 않더라도 유의미한 정보를 제공해주는게 디버깅에 용이할듯
Heterogeneous enums
enum BooleanLikeHeterogeneousEnum {
No = 0,
Yes = "YES",
}
값을 문자와 숫자를 섞어서 사용
런타임에서 열거형
enum E {
X, Y, Z
}
function f(obj: { X: number }) {
return obj.X;
}
f(E);
런타임에 존재하는 실제 객체이다
"use strict";
var E;
(function (E) {
E[E["X"] = 0] = "X";
E[E["Y"] = 1] = "Y";
E[E["Z"] = 2] = "Z";
})(E || (E = {}));
JS 변환 코드
역 매핑
숫자 열거형은 역 매핑을 받는다.
JS 변환 코드
enum Enum {
X, Y, Z
}
function f(obj: { X: number }) {
return obj.X;
}
let x = Enum.X;
let nameOfX = Enum[x]; // "X"
"use strict";
var E;
(function (E) {
E[E["X"] = 0] = "X";
E[E["Y"] = 1] = "Y";
E[E["Z"] = 2] = "Z";
})(E || (E = {}));
정방향 (name -> value) 과 역방향 (value -> name) 모두 가능
런타임에서 열거형
enum E {
X = 'x',
Y = 'y',
Z = 'z'
}
function f(obj: { X: string }) {
return obj.X;
}
f(E);
런타임에 존재하는 실제 객체이다
"use strict";
var E;
(function (E) {
E["X"] = "x";
E["Y"] = "y";
E["Z"] = "z";
})(E || (E = {}));
JS 변환 코드
Const enums
const enum Directions {
Up,
Down,
Left,
Right
}
let directions = [
Directions.Up,
Directions.Down,
Directions.Left,
Directions.Right
]
const 열거형은 사용공간에 인라인 된다
"use strict";
let directions = [
0 /* Up */,
1 /* Down */,
2 /* Left */,
3 /* Right */
];
JS 변환 코드
💁♂️ 데코레이터
Class Decorator
클래스 선언 직전에 선언, 클래스의 생성자를 인수로 전달 받음
@sealed
class Greeter {
greeting: string;
constructor(message: string) {
this.greeting = message;
}
greet() {
return "Hello, " + this.greeting;
}
}
function sealed(constructor: Function) {
Object.seal(constructor);
Object.seal(constructor.prototype);
}
Method Decorator
메서드 선언 직전에 선언
class Greeter {
greeting: string;
constructor(message: string) {
this.greeting = message;
}
@enumerable
greet() {
return "Hello, " + this.greeting;
}
}
// 정적 맴버에 대한 클래스 생성자 함수, 멤버 이름, 프로퍼티 설명자
function enumerable (
target: any, propertyKey: string, descriptor: PropertyDescriptor) {
descriptor.enumerable = true;
};
Property Decorator
프로퍼티 선언 직전에 선언
class Greeter {
@writable
greeting: string;
constructor(message: string) {
this.greeting = message;
}
greet() {
return "Hello, " + this.greeting;
}
}
function writable (target: any, propertyKey: string) {
// description 역할
return {
writable: true
}
};
Parameter Decorator
매개변수 선언 직전에 선언
class Greeter {
greeting: string;
constructor(message: string) {
this.greeting = message;
}
greet(@pringInfo name: string) {
return "Hello, " + name + this.greeting;
}
}
function pringInfo (target: any, methodName: string, paramIndex: number) {
console.log(target) // 클래스 프로토타입, {constructor: f, greet: f}
console.log(methodName) // 아름, greet
console.log(paramIndex) // 매개변수 index, 0
};
Decorator Factories
데코레이터 선언에 적용되는 방식을 변경하는 방법
function color(value: string) {
return function (target) {
// 'target'과 'value' 변수를 가지고 무언가를 수행합니다.
}
}
단순히 데코레이터가 런타임헤 호출한 표현식을 반환하는 함수
Decorator Composition
데코레이터는 위에서 아래로 평가되며 결과는 아래에서 위로 호출
function f() {
console.log("f(): evaluated");
return function (target, propertyKey: string, descriptor: PropertyDescriptor) {
console.log("f(): called");
}
}
function g() {
console.log("g(): evaluated");
return function (target, propertyKey: string, descriptor: PropertyDescriptor) {
console.log("g(): called");
}
}
class C {
@f()
@g()
method() {}
}
f(): evaluated
g(): evaluated
g(): called
f(): called
최적 공통 타입 (Best common type)
여러 표현식에서 타입 추론이 발생할 때, 최적 공통 타입을 계산한다
let x = [0, 1, null];
x = [null]
x = [0, 1, 2]
x = [null, 1 ,2]
// Type 'string' is not assignable to type 'number | null'.
x = ['후보에 없음']
최적 공통 타입이 존재하지 않으면 추론의 결과는 유니언 배열 타입
- 러닝커브
- 정적 타이핑으로 인한 코드량
- d.ts (ts 지원여부)
- any 타입과 ts-ignore의 난무
- 프로젝트 규모
- JS의 장점 (유연함)
Typescript를 사용해보니..
sat10am-200718
By Jaewoo KIM
sat10am-200718
- 318