자바스크립트의 특징인 동적 타이핑을 이용하기 위해
런타임 단계에서 타입 에러를 확인하고 고칠 수 있다.
컴파일 단계에서 타입 에러를 미리 확인할 수 있습니다.
타입을 하나하나 명시해야 하기 때문에 개발 속도는 오래 걸린다.
타입스크립트를 이용하면 유지 보수가 유리하기 때문에 개발 속도도 자연스럽게 빨라진다.
마이크로소프트에서 자바스크립트의 한계를 해결하기 위해 개발한 상위 집합 오픈 소스 언어
자바스크립트 -> 타입스크립트 : 컴파일러 필요x
타입스크립트 -> 자바스크립트 : 컴파일러 필요o
타입을 미리 지정해두어 각 타입에 맞는 변수만을 입력할 수 있도록 한다.
const sum = (a: number, b: numder) => {
return a+b
}
sum(1,2) // 3
--------------------------------------------
만약 b에 string 값을 할당할 시,
// type 'string' is not assignable to type 'number' .ts(2322)
가 출력된다.
TS는 JS코드에 변수나 함수 등 type을 정의
Type을 나타내기 위해 타입표기(type annotation)
TS의 Type: 기본자료형(primitive type), 참조 자료형(reference type), 추가 제공 자료형
object와 reference 형태가 아닌 실제 값을 저장하는 자료형
primitive type 내장 함수를 사용 가능한 것은 자바스크립트의 처리 방식 덕분
종류: string, boolean, number, null, undefined, symbol(ES6 추가)
let str: string = "hi"; // string: 문자열 저장
let n: null = null; // null: 값이 의도적으로 비어 있는 상태를 저장
let u: undefined = undefined; // undefined: 아무 값이 할당되지 않은 상태를 저장
// typeof로 데이터 타입 확인
typeof null; // 'object'
typeof undefined; // 'undefined'
null===undefined // false
null==undefined // true
null===null // true
null==null // true
!null // true
isNan(1+null) //false
isNan(1+undefined) // true
객체, 배열, 함수 등과 같은 object 형식의 타입
메모리에 값을 주소로 저장하고 출력 시 메모리 주소와 일치하는 값을 출력
종류: object(기본 자료형에 해당하지 않는 타입), array, function
function create(o: object): void{}
create({prop: 0}) // 성공
create([1,2,3]) // 성공
// create()에 string, boolean, number, null, undefined를 할당하면 error
let arr: number[]=[1,2,3] // array: 배열을 저장하는 타입
let arr: Array<number>=[1,2,3] // generic을 사용한 표기 가능
추가 제공 자료형: ts에서 개발자의 편의를 위해 추가로 제공하는 타입
종류: tuple, enum, any, void, never
let arr: [string,number]=["Hi",6];
arr[1].concat("!"); // error, 'number' does not have 'concat'
arr[3]="hello"; // 정의하지 않은 index 호출 시 오류
// error,property '3' does not exist on type '[string,number]'
enum Car {BUS, TAXI, SUV};
let bus: Car = Car.BUS;
let bus: Car = Car[0]; // 인덱스 번호로 접근
enum Car { BUS = 1, TAXI = 2, SUV = 3}; // 인덱스를 사용자 임의로 변경
let taxi: String = Car[2];
enum Car {BUS = 2, TAXI, SUV};
let taxi: String = Car[3];
let str: any = "hi";
let num: any = 10;
let arr: any = ["a", 2, true];
let unknown: void = undefined;
function sayHi(): void{ // 변수에는 undefined와 null만 할당하고,
console.log("hi"); // 함수에는 반환 값을 설정할 수 없는 타입
}
function neverEnd(): never{
while(true){} // 항상 오류를 발생시키거나 절대 반환하지 않는 반환 타입
} // 종료되지 않는 함수
function neverEnd(): never{
while(true){ // cannot have a reachable end point.ts(2534)
break;
}
}
function error(message: string): never{
throw new Error(message);
}
TS는 공통 타입 변환을 용이하게 하기 위해 유틸리티 타입을 제공
유틸리티 타입은 전역으로 사용 가능
종류
Patial<T>, Readonly<T>
Record<T> ,Pick<T,K>
Omit<T,K>, Exclude<T,U>, Extract<T,U>
NonNullable<T>, Parameters<T>,ConstructorParameters<T>
Return Type<T>, Required<T>
Patial<T>
프로퍼티를 선택적으로 만드는 타입을 구성
주어진 타입의 모든 하위 타입 집합을 나타내는 타입을 반환
interface Todo{ // Todo 타입 지정
title: string;
description: string;
}
function updateTodo(todo: Todo, fieldsToUpdate: Partial<Todo>){
return{...todo,...fieldsToUpdate};
}
const todo1 = {
title: '멋진 하루 보내기',
description: '끝장나게 숨쉬기',
};
const todo2 = updateTodo(todo1, {
description: '집청소하기',
});
Readonly<T>
프로퍼티를 읽기 전용으로 설정한 타입을 구성
interface Todo{
title: string;
}
const todo: Readonly<Todo> = {
title: '읽기만 가능합니다',
};
todo.title = '쓰고싶어요';
// error: cannot as sign to 'title' because it is a read-only property
Record<T>
프로퍼티의 집합 K로 타입을 구성.
타입의 프로퍼티들을 다른 타입에 매핑
interface PageInfo{
title: string;
}
type Page = 'home' | 'about' | 'contact';
const x: Record<Page, PageInfo> = {
about: {title: 'about'},
contact: {title: 'contact'},
home: {subTitle: 'home'}, // error: '{subTitle: string;}' is not assignable
main:{title: 'home'}, // error: main is not assignable to type 'Page'
};
Pick<T,K>
프로퍼티 K의 집합을 선택해 타입을 구성
interface Todo{
title: string;
description: string;
completed: boolean;
}
type TodoPreview = Pick<Todo, 'title' | 'completed'>;
const todo: TodoPreview = {
title: '취업',
completed: false,
description: 'description'
// error: 'description' is not assignable to type
}
Omit<T,K>
모든 프로퍼티를 선택한 다음 K를 제거한 타입을 구성
interface Todo{
title: string;
description: string;
completed: boolean;
}
type TodoPreview = Omit<Todo, 'description'>;
const todo: TodoPreview = {
title: 'Clean room',
completed: false,
description: 'description'
// error: 'description' is not assignable to type
}
T에서 U에 할당할 수 있는 모든 속성을 제외한 타입을 구성
type T0 = Exclude<"a"|"b"|"c", "a">; // "b"|"c"
type T1 = Exclude<"a"|"b"|"c", "a"|"b">; // "c"
type T2 = Exclude<string|number|(() => void), Function>; // string|number
T에서 U에 할당할 수 있는 모든 속성을 추출하여 타입을 구성
type T0 = Extract<"a"|"b"|"c", "a"|"f">; // "a"
type T1 = Extract<string|number|(() => void), Function>; // () => void
NonNullable<T>
null과 undefined를 제외한 타입
type T0 = NonNullable<string|number|undefined>; // string|number
type T1 = NonNullable<string[]|null|undefined>; // string[]
Parameters<T>
함수 타입 T의 매개 변수 타입들의 튜플 타입을 구성
declare function f1(arg: {a: number, b: string}): void
type T0 = Parameters<() => string>; // []
type T1 = Parameters<(s: string) => void>; // [string]
type T2 = Parameters<(<T>(arg: T) => T)>; // [unknown]
type T3 = Parameters<typeof f1>; // [{a: number, b: string}]
type T4 = Parameters<any>; // unknown[]
type T5 = Parameters<never>; // never
type T6 = Parameters<string>; // 오류
type T7 = Parameters<Function>; // 오류
ConstructorParameters<T>
생성자 함수 타입의 모든 매개 변수 타입을 추출
모든 매개 변수 타입을 가지는 튜플 타입(T가 함수가 아닌 경우 never)를 생성
type T10 = ConstructorParameters<ErrorConstructor>; // [(string | undefined)?]
type T1 = ConstructorParameters<FunctionConstructor>; // string[]
type T2 = ConstructorParameters<RegExpConstructor>;
// [string, (string | undefined)?]
interface I1{
new(args: string): Function;
}
type T12 = ConstructorParameters<I1>; // [string]
function f1(a: T12){
a[0]
a[1] // error: tuple type '[args: string]' of length '1' has no element at index '1'.
}
Return Type<T>
함수 T의 반환 타입으로 구성된 타입을 생성
declare function f1(): {a: number, b: string}
type T0 = ReturnType<() => string>; // string
type T1 = ReturnType<(s: string) => void>; // void
type T2 = ReturnType<(<T>() => T)>; // {}
type T3 = ReturnType<(<T extends U, U extends number[]>() => T)>; // number[]
type T4 = ReturnType<typeof f1>; // {a: number, b: string}
type T5 = ReturnType<any>; // any
type T6 = ReturnType<never>; // any
type T7 = ReturnType<string>; // 오류
type T8 = ReturnType<Function>; // 오류
Required<T>
T의 모든 프로퍼티가 필수로 설정된 타입을 구성
interface Props{
a?: number;
b?: string
};
const obj: Props = {a:5};
const obj2: Requires<Props> = {a:5};
// Error: property 'b' is missing in type '{a: number;}'
매개변수: 함수를 정의할 때 사용되는 값
인수: 함수를 호출할 때 사용되는 값
인자값 == 매개변수 == Parameter
일급객체: 다른 객체들에 일반적으로 적용 가능한 연산을 모두 지원하는 객체
JS와 TS의 함수 == 일급객체
function hello(name){
return `hello ${name}`;
}
let hello = function(name){
return `hello ${name}`;
}
let hello = (name) =>{
return `hello ${name}`;
}
let hello = (name) => `hello ${name}`;
let hello = new Function("name", 'return"hello"+name');
// 함수 생성자는 되도록 사용 권장x
TS 함수 작성 시 반환 타입을 추론하도록 작성하는 걸 권장
함수의 매개 변수와 인수의 타입이 호환 가능하게 작성
인수의 타입을 잘못 전달하면 에러 발생
TS 컴파일러는 방정식의 한쪽에만 타입이 있더라도 타입 추론 가능 = contextual typing
=> 프로그램에서 타입을 유지하기 위한 노력을 줄일 수 있다.
// 일반적인 함수
let f12 = function (x: number, y: number): number{
return x+y;
}
// contextual typing
let f12: (baseValue: number, increment: number) => number = function (x,y){
return x+y;
}
함수에 주어진 인자의 수는 함수가 기대하는 매개변수의 수와 일치해야 한다.
function buildName(firstName:string, lastName: string){
return firstName + " " + lastName;
}
let result = buildName("Kim", "Bero");
js에서는 모든 매개변수가 선택적으로, 인수가 없다면 undefined가 된다.
ts에서도 변수명 뒤에 ? 를 붙여 선택적 매개변수 사용 가능
function buildName(firstName:string, lastName?: string){
if(lastName) return firstName + " " + lastName;
else return firstName;
}
let result1 = buildName("Kim");
let result2 = buildName("Kim", "Bero");
ts에서는 값을 제공하지 않거나, undefined로 했을 때에 매개변수의 값 할당 가능
function buildName(firstName:string, lastName: "Beri"){
return firstName + " " + lastName;
}
let result1 = buildName("Kim"); // "Kim Beri"
let result2 = buildName("Kim", undefined); // "Kim Beri"
let result3 = buildName("Kim", "Bero"); // "Kim Bero"
컴파일러는 생략부호 (...) 뒤의 인자 배열을 빌드해 함수에서 사용
나머지 매개변수는 매개변수의 수를 무한으로 취급
아무것도 넘겨주지 않을 수도 있다.
function buildName(firstName:string, ...restOfName: string[]){
return firstName + " " + restOfName.join(" ");
}
let hamsterName = buildName("Bero", "Beri", "Bebe", "Cream");
// "Bero Beri Bebe Cream"
타입스크립트에서는 값을 제공하지 않거나, undefined로 설정한 매개변수의 값에 기본 값을 할당할 수 있습니다.
OOP는 컴퓨터 프로그램을 객체의 모임으로 파악하려는 프로그래밍 패러다임
객체들은 서로 메세지를 주고 받을 수 있으며 데이터를 처리할 수 있다.
프로그램을 유연하고 변경이 용이하게, 개발과 보수를 간편하게, 직관적인 코드 분석 가능
객체 지향 프로그래밍의 중요한 특성: 강한 응집력과 약한 결합력 지향
객체를 정의하는 틀 또는 설계도와 같은 의미로 사용
클래스의 요소: 멤버(필드, 생성자, 메소드)
인스턴스: new 연산자에 의해 생성된 객체
class Person{
name: string;
constructor(name:string){
this.name = name;
}
say(){
return "Hello, My name is" + this.name;
}
}
let person = new Person("Bibi");
속성 또는 메소드로의 접근을 제한하기 위해 사용
ts에는 public > protected > private 3종류의 접근 제어자가 존재
java와 다르게 package 개념이 없어 default 접근 제어자는 존재하지 않는다.
class Animal {
public name: string
constructor(theName: string){
this.name = theName;
}
}
new Animal("Cat").name;
class Animal {
private name: string
constructor(theName: string){
this.name = theName;
}
}
new Animal("Cat").name;
// error: property 'name' is private and only accessible within class 'Animal'
멤버가 포함된 클래스와 그 하위 클래스 외부에서의 접근을 막는다.
Person에서 파생된 Employee의 인스턴스 메소드에서는 name을 사용할 수 있다.
class Person{
protected name: string;
constructor(name:string){
this.name = name;
}
}
class Employee extends Person{
private department: string
constructor(name: string, department: string){
super(name);
this.department = department;
}
public getElevatorPitch(){
return `Hello, my name is ${this.name} and I work in ${this.department}.`;
}
}
let howard = new Employee("Howard", "Sales");
console.log(howard.getElevatorPitch());
console.log(howard.name); // error
OOP는 상속을 이용하여 존재하는 클래스를 확장해 새로운 클래스를 만들 수 있다.
파생된 클래스는 하위클래스(subclass), 기초 클래스는 상위클래스(superclass)
class Animal {
move(distanceInMeters: number){
console.log(`Animal moved ${distanceInMeters}m.`);
}
}
class Dog extends Animal{
// extends 키워드로 Animal이라는 기초 클래스에서 Dog 클래스가 파생
makeSound(){
console.log("멍멍!");
}
}
class Cat extends Animal{
makeSound(){
console.log("야옹!");
}
}
const dog = new Dog();
dog.move(10);
dog.makeSound();
const cat = new Cat();
cat.move(5);
cat.makeSound();
Dog, Cat은 Animal의 기능(move 메소드)을 확장하기 때문에 move()와 makeSound()를 가진 인스턴스를 생성
비공개로 설정하려는 속성은 private로 설정하고 속성값을 읽고 수정하는 getter/setter 함수를 사용
class Person{
private _name: string
get name(){
return this._name;
}
set name(name: string){
if(name.length > 10){
throw new Error("name too long")
}
this._name = name;
}
}
let person = new Person();
console.log(person.name); // undefined
person.name = "june";
console.log(person.name) // june
person.name = "junejunejunejune"; // throw error
읽기만 가능한 속성을 선언
class Person{
readonly age: number = 20 // 선언 초기화
constructor(age: number){
this.age = age;
}
}
let person = new Person(10); // 생성자 초기화
person.age = 30;
// error: cannot assign to 'age' because it is a read-only property
전역멤버(객체마다 할당되지 않고 클래스의 모든 객체가 공유하는 멤버)를 선언
class Grid{
static origin = {x: 0, y: 0}
calculateDistanceFromOrigin(point: {x: number; y: number}){
let xDist = point.x - Grid.origin.x;
let yDist = point.y - Grid.origin.y;
return Math.sqrt(xDist * xDist + yDist * yDist) / this.scale;
}
constructor(public scale: number){}
}
let grid1 = new Grid(1.0) // 1xscale
let grid2 = new Grid(5.0) // 5xscale
console.log(grid1.calculateDistanceFromOrigin({x: 10, y: 10}));
console.log(grid2.calculateDistanceFromOrigin({x: 10, y: 10}));
④ static
static은 각 인스턴스가 아닌 클래스 자체에서 보이는 전역 멤버를 생성합니다. 그래서 범용적으로 사용되는 값에 static을 설정하여 사용합니다.
다른 클래스들이 파생될 수 있는 기초 클래스로, 직접 인스턴스화 불가능
abstract 키워드: 추상 클래스나 추상 메소드를 정의하는 데 사용
추상 메소드는 클래스에는 구현되어 있지 않고 파생된 클래스에서 구현해야 한다.
abstract class Animal{
protected name: string
constructor(name: string){
this.name = name;
}
abstract makeSound(): void
move(): void{
console.log("move!!");
}
}
class Dog extends Animal {
constructor(name: string) {
super(name); // 파생된 클래스의 생성자는 반드시 super()를 호출
}
makeSound(): void { // 반드시 파생된 클래스에서 구현 필수
console.log(this.name + " 멍멍!!")
}
}
const animal = new Animal("animal");
// error: cannot create an instance of an abstract class
const dog = new Dog("진돗개")
dog.makeSound() // 진돗개 멍멍!!
프로그램의 일부분을 서브 클래스로 캡슐화해 특정 단계의 기능을 바꾸는 것
전체적인 알고리즘은 상위 클래스에서 구현, 다른 부분은 하위 클래스에서 구현
코드 중복을 최소화할 수 있다.
abstract class Parent{ // 템플릿 메소드: 자식에서 공통적으로 사용하는 부분
public do(){
console.log("Parent에서 실행 - 상");
this.hook(); // 훅 메소드: Child에서 구현해야 할 부분
console.log("Parent에서 실행 - 하");
}
abstract hook(): void
}
class Child extends Parent{
hook(): void{
console.log("Child");
}
const child = new Child();
child.do();
// 실행 결과
// Parent에서 실행 - 상
// Child
// Parent에서 실행 - 하