https://www.typescriptlang.org 에서 타입스크립트를 설치하지 않고 문법을 연습할 수 있습니다.
?
를 넣어서 Optional 프로퍼티로 만듭니다.
Optional
은 초기화하지 않고 접근한다면 undefined
을 반환합니다.
type Player = { // { } => object
name: string,
age?: number
};
const person1: Player = {
name: "John"
};
const person2: Player = {
name: "Jane",
age: 25
};
function greet(name: string, age?: number) {
console.log(`Hello, ${name}! ${age ? `You are ${age} years old.` : ""}`);
}
greet("John"); // Hello, John!
greet("Jane", 25); // Hello, Jane! You are 25 years old.
&&
연산자로 안전하게 사용할 수 있습니다.
if( person1.age && person1.age < 20) { // undefined 방지
console.log("Yor are teenager.")
}
또는 ?.
를 이용할 수 있습니다.
const address = person2.address?.city; // undefined 반환
if (address === undefined) {
console.log("Result is undefined."); // Result is undefined.
}
함수에도 타입을 명시할 수 있습니다.
if( person2.age && person2.age > 20) {
const answer = playerMaker(person1.name);
const returnAge = ageReturn(person2.age);
answer.age = 30; // type: Player
console.log(`${answer.name} -> ${answer.age}, ${person2.name} -> ${returnAge}`)
} // "John -> 30, Jane -> 25"
function playerMaker(name: string): Player { // 명시
return {
name
}
}
function ageReturn(age: number) {
return age;
}
위 코드를 보면 타입스크립트에서 함수 선언문은 호이스팅이 가능하지만 함수 표현식은 호이스팅이 안되므로 먼저 선언해야 합니다.
const playerMaker = (name: string): Player => ({name})
const answer = playerMaker("Niko");
불변으로 만들어서 읽기만 가능하도록 합니다.
type Player = {
readonly name: string,
age?: number
};
const person1: Player = {
name: "John"
};
const playerMaker = (name: string): Player => ({name})
const answer = playerMaker(person1.name);
answer.name = "Kane"; // Cannot assign to 'name' because it is a read-only property.
const numberArr : readonly number[] = [1, 2, 3, 4]
numberArr.push(5); // Property 'push' does not exist on type 'readonly number[]'.
const person: [string, number, boolean] = ["John", 25, true];
console.log(person[0]); // "John"
console.log(person[1]); // 25
타입 시스템을 통해 타입을 조작하고 변환하는 기능을 제공합니다
타입스크립트가 사용하지 못하는 unknown
타입( 명시 )을 타입연산자로 정제하여 사용할 수 있습니다.
타입을 문자열로 반환합니다.
const x = 10;
console.log(typeof x); // "number"
let a: unknown;
if(typeof a === 'number'){
let b = a + 1
}
객체 타입의 모든 속성 이름을 유니온 타입으로 반환합니다.
type Person = {
name: string;
age: number;
};
type PersonKeys = keyof Person; // "name" | "age"
객체가 특정 속성을 가지고 있는지 확인합니다.
type Person = {
name: string;
age?: number;
};
type HasAge = "age" in Person; // true
아래 함수는 값을 반환하지 않습니다.
function throwError(msg: string): never {
throw new Error(msg)
}
아래의 타입가드에서 걸러지지 않고 else문으로 간다면 never타입을 반환하므로 리턴되지 않습니다.
function validate(value: string | number): string {
if (typeof value === "string") {
return value.toUpperCase(); // string
} else if (typeof value === "number") {
return value.toFixed(2); // number, toFixed( 3.1415 -> 3.14 반올림 )
} else {
const _exhaustiveCheck: never = value; // value 는 never 타입
throw new Error(`Unexpected value: ${_exhaustiveCheck}`);
}
}
변수에 마우스를 올리면 Call signature
를 확인할 수 있습니다.
함수의 타입을 정의할때 매개변수와 반환 타입을 지정하면 해당 타입의 함수는 타입을 지정하지 않아도 됩니다.
type Add = (x: number, y: number) => number;
const add: Add = (a, b) => a + b
유니언 타입을 이용해서 함수를 오버로딩합니다.
type ConcatFunction = {
(x: string, y: string): string;
(x: number, y: number): number;
(x: string | number, y: string | number): string | number;
};
const concat: ConcatFunction = (x: string | number, y: string | number): string | number => {
if (typeof x === "string" && typeof y === "string") {
return x + y;
} else if (typeof x === "number" && typeof y === "number") {
return x + y;
} else {
throw new Error("Invalid arguments");
}
};
console.log(concat("Hello, ", "World")); // "Hello, World"
console.log(concat(5, 10)); // 15
라우팅시 오버로딩을 자주 이용합니다.
type Config = {
path: string,
state: object
}
type Push = {
(path: string): void
(config: Config): void
}
const push: Push = (config) => {
if(typeof config === 'string') console.log(config)
else console.log(config.path)
}
const test = "/home"
const configTest = {
path: "/join",
state: {}
}
push(test) // "/home"
push(configTest) // "/join"
매개변수의 갯수가 다르게 오버로딩될 경우는 optional을 이용합니다.
type Add = {
(a: number, b: number): number,
(a: number, b: number, c:number): number
}
const add: Add = (a, b, c?: number) => {
if(c) return a + b + c
return a + b
}
console.log(add(1,2,4)) // 7
오버로딩의 경우가 많아지거나 복잡해질경우 제네릭을 사용해서 한번에 받도록 할 수 있습니다.
type SuperPrint = {
<T>(arr: T[]): void
}
const superPrint: SuperPrint = (arr) => {
arr.forEach(i => console.log(i))
}
superPrint([1,2,3,4])
superPrint([true, false, "happy"])
제네릭 함수
function superPrint<T>(a: T[]){
return a[0]
}
const b = superPrint([true, false, false])
console.log(b) // true
제네릭 타입
type Player<T> = {
name: string,
extra: T
}
const doe: Player<{food: string}> = {
name: "doe",
extra: {
food: "Hamberger"
}
}
배열 선언
type A = Array<number>
let a: A = [1, 2, 3, 4]
자바스크립트 코드로 컴파일 될때 abstract
나 접근 제한자는 js코드에 보이지 않습니다.
추상클래스는 공통된 특성을 상속클래스에 전달하므로 상속 클래스는 멤버변수를 추가하지 않아도 됩니다.
또한 추상클래스는 인스턴스를 만들 수 없습니다.
abstract class User {
constructor(
private firstName: string,
private lastName: string,
public nickNama: string
){} // 자바스크립트로 컴파일하면 this.a = a 를 만들어 준다.
getFullName(){
// return this.firstName +" "+ this.lastName // Amiah Miller
return `${this.firstName} ${this.lastName}`
}
}
class MyPlayer extends User{
}
const nicol = new MyPlayer("Amiah", "Miller", "22")
console.log(nicol.getFullName()) // Amiah Miller
추상 클래스를 만들게 되면 상속하는 클래스에서 구현해야 합니다.
abstract class User {
// 생략
abstract getNickName(): string
}
class MyPlayer extends User{
getNickName(){
return this.nickName
}
}
const nicol = new MyPlayer("Amiah", "Miller", "22")
console.log(nicol.getNickName()) // 22
type Words = {
[key: string]: number // key - value
}
class Dict {
private words: Words
constructor(){
this.words = {} // 빈 객체로 초기화
}
add(word: Word){
if(this.words[word.term] === undefined)
this.words[word.term] = 1
}
count(term: string){
return this.words[term]
}
}
class Word {
constructor(
public term: string,
// public readonly term: string,
public count: number
){}
}
const tomato = new Word("토마토", 1)
const dict = new Dict() // dict.words -> 빈 객체
dict.add(tomato)
console.log(dict.count("토마토")) // 1
string
, number
같은 구체적인 타입(concrete type)이 아닌 개발자가 커스텀한 타입으로 만들 수 있습니다.
type Team = "red" | "blue" | "yellow"
type OnePlayer = {
nickname: string,
team: Team
}
const jack: OnePlayer = {
nickname: "jack",
team: "blue"
}
위 코드를 인터페이스로 바꿀 수 있습니다.
interface OnePlayer {
nickname: string,
team: Team
}
인터페이스를 통해 객체의 구조를 강제할 수 있습니다.
구조를 강제하므로 상속하는 클래스는 동일한 멤버변수와 메소드를 구현해야 합니다.
interface Person {
name: string;
age: number;
greet(): void; // abstract
}
interface Human {
health: number
}
class Student implements Person, Human {
constructor(
public name: string,
public age: number,
public health: number
) {}
greet() {
console.log(`Hello, my name is ${this.name}.`);
}
}
const student = new Student("Alice", 20, 10);
console.log(student.name); // Alice
console.log(student.age); // 20
student.greet(); // Hello, my name is Alice.
인터페이스를 구현하는 클래스의 멤버변수는 public이 되어야 합니다.
또한 타입스크립트에서 인터페이스는 추상 클래스와 다르게 자바스크립트 코드로 컴파일 되지 않습니다.
제네릭과 인터페이스를 함께 이용해서 사용할 수 있습니다.
interface MyStorage<T> {
[key: string]: T
}
class LocalStorage<T> {
private storage: MyStorage<T> = {
}
public set(str: string, value: T){
this.storage[str] = value
}
public clear(){
this.storage = {}
}
public get(str: string) {
return this.storage[str]
}
}
const homeStorage = new LocalStorage<number>();
homeStorage.set("서랍", 1203)
console.log(homeStorage.get("서랍")) // 1203
homeStorage.clear()
console.log(homeStorage.get("서랍")) // undefined