타입스크립트 코드 테스트
https://www.typescriptlang.org/play

 

타입스크립트 핸드북
https://typescript-kr.github.io/pages/basic-types.html

 

 

타입스크립트란?

  1. TypeScript는 JavaScript에 추가적인 구문을 추가하여 editor와의 단단한 통합을 지원합니다. editor에서 초기에 오류를 잡을 수 있습니다.
  2. TypeScript 코드는 JavaScript가 실행되는 모든 곳(브라우저, Node.js 또는 Deno 및 앱 등)에서 JavaScript로 변환될 수 있습니다
  3. TypeScript는 JavaScript를 이해하고 타입 추론(type inference)을 사용하여 추가 코드 없이도 훌륭한 도구를 제공합니다.

Types of TS(기본)

 

  • 배열: 자료형[]
  • 숫자: number
  • 문자열: string
  • 논리: boolean
  • optional
// Alias 타입
const player : {
    name: string,
    age?:number // 선택적 타입
} = {
    name: "nico"
}

if(player.age && player.age >10) {
    console.log("어린이")
}

 

예시 설명: name은 반드시 string, age는 undefined | number임

if문에서 player.age를 조건으로 쓰려면 player.age가 undefind가 아닐 경우를 상정해서 조건을 걸어야 

 

 

// Alias 타입
type Player = {
    name: string,
    age?: number // 선택적 타입
}

const player : Player = {
    name: 'nico'
}

const player2 : Player = {
    name: 'meme',
    age: 15

}

if(player.age && player.age >10) {
    console.log("어린이")
}

// aregument의 타입을 지정하는 방법 => (name: string)
// return값의 타입을 지정하는 방법 => : Player
function playerMaker(name: string) : Player {
    return {
        name
    }
}
//위와 같은 함수
const playerMaker = (name:string) : Player => ({item})

const nico = playerMaker("nico")
nico.age = 12

 

 

readonly 

 

// Alias 타입
type Player = {
    readonly name: string,
    age?: number // 선택적 타입
}

nico.name = "ds"

const numbers: readonly number[] = [1,2,3,4]
number.push(5)

 

예시 설명: nico.name은 readonly라서 저렇게 하면 오류남

number.push(5) 안 됌 

 

 

unknown

 

let a: unknown;

if(typeof a === 'number') {
let b = a + 1
}
if(typeof a === "string") {
    let b = a.toUpperCase()
}

 

변수의 타입을 미리 알지 못 할 때  unknown을 사용

 

 

void

 

void는 아무것도 return하지 않는 함수를 대상으로 사용

 

function hello() {
    console.log('x')
}

 

 

never

 

함수가 절대 return하지 않을 때 발생

 

function hello():never {
    throw new Error("xxx") //에러를 발생시키는 함수
}

 

function hello(name:string|number) {
    if(typeof name === "string") {
        name //string
    } else if (typeof name === "number") {
        name //number
    } else {
        name //never
    }
}

 

 

call signatures

 

type Add = (a:number, b:number) => number;

const add:Add = (a, b) => a + b

 

 

overloading

 

직접 작성하기보다 외부 라이브러리에 자주 보이는 형태로, 하나의 함수가 복수의 Call Signature를 가질 때 발생

 

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)} //string
    else {
        console.log(config.path) //config
    }
}

 

type Add = {
    (a:number, b:number) : number
    (a:number, b:number, c:number) : number

}

const add:Add = (a,b,c?:number) => { //c는 선택사항
    if(c) return a + b + c
    return a + b
}

 

 

Polymorephism(다형성)과 Generic(제네릭) 

 

type SuperPrint = {
    <T>(arr:T[]):void //제네릭
}

const superPrint: SuperPrint = (arr) => {
    arr.forEach(i => console.log(i))
}

superPrint([1, 2, 3, 4])
superPrint([true, false, true])
superPrint(["a", "b"])
superPrint(["a", 1, 2, false])

 

type SuperPrint = {
    <T>(arr:T[]):T
}

const superPrint: SuperPrint = (arr) => arr[0]

const a = superPrint([1, 2, 3, 4])
const b = superPrint([true, false, true])
const c = superPrint(["a", "b"])
const d = superPrint(["a", 1, 2, false])

 

type SuperPrint = {
    <T, M>(arr:T[], b:M):T //타입스크립트에게 T는 함수의 첫번째 파라미터로 배열이 올거라고 말해주고 M은 두번쨰 파라미터로 들어온다고 말해
}

const superPrint: SuperPrint = (arr) => arr[0]

const a = superPrint([1, 2, 3, 4], true)
const b = superPrint([true, false, true], "a")
const c = superPrint(["a", "b"], 1)
const d = superPrint(["a", 1, 2, false], 1)

 

 

 

// 타입을 만드는 방법
type Words = {
    [key:string]:string //프로퍼티의 이름은 모르지만 타입은 알 때 씀 
}

class Dict {
    private words: Words //프로퍼티를 만들고 원하는 대로 초기화를 해주면 됌 
    constructor(){
        this.words = {}
    }
    add(word: Word){ //클래스는 타입처럼 쓸 수 있음
        if(this.words[word.term] === undefined) {
            this.words[word.term] = word.def
        }
    }
    def(term:string){
        return this.words[term]
    }
}

class Word {
    constructor(
        public term:string,
        public def:string
    ) {}
}

const kimchi = new Word("kimchi", "한국의 음식")

const dict = new Dict()
// dict.add(kimchi)
dict.def("kimchi")

 

 

type Words = { // 해시
    [key: string]: (string | string[])
    //객체의 property에 대해 모르지만 타입만을 알 때 유용하다
}

class Dict {
    private words: Words
    constructor() {
        this.words = {}
    }

    add(word: Word) { // word는 Word 클래스의 인스턴스 타입.
        if(!this.words[word.term]) {
            // 사전에 없는 단어이면
            this.words[word.term] = word.def;
        }
    }

    find(term: string) {
    return this.words[term];
    }

    // 단어를 삭제
    rmv(term: string) {
    delete this.words[term];
    }

    // 단어 이름 업데이트
    update(oldTerm: string, newTerm: string) {
        if(this.words.hasOwnProperty(oldTerm)) {
            this.words[newTerm] = this.words[oldTerm];
            delete this.words[oldTerm];
        }
    }

    // 사전에 저장된 단어의 개수
    size() {
    return Object.keys(this.words).length;
    }

    // 모든 사전의 이름과 뜻 출력
    all() {
        for(let [key, value] of Object.entries(this.words)) {
            console.log(`${key}: ${value}`)
        }
    }
}

// words는 initializer 없이 선언해주고 contructor에서 수동으로 초기화
// constructor에 인자로 넣어 constructor가 지정해주길 바라는 게 아니므로
// 각각의 단어에 대한 클래스

class Word {
    constructor(
        public term: string, public def: (string| string[])
    ) {}

    // 단어 출력하는 메소드
    toString() {
        console.log(`${this.term}: [뜻] ${this.def}`);
    }

    // 단어 정의 추가
    addDef(newDef : string) {
        if(typeof this.def === 'string') {
            this.def = [this.def, newDef]
        } else {
            this.def = [...this.def, newDef];
        }
    }

    // 단어 정의 수정
    updateDef(oldDef : string, newDef: string) {
        if(typeof this.def === 'string') {
            if(oldDef === this.def) this.def = newDef
            } else {
            this.def.filter(val => val !== oldDef);
        this.def.push(newDef);
        }
    }
}

/** 출력 */
const kimchi = new Word("kimchi", "한국의 음식");
const tang = new Word("연근 갈비탕", "중국의 음식");
const sushi = new Word("스시", "일본의 음식");
kimchi.addDef("고춧가루로 배추를 버무려 숙성 및 발효시킨 음식")
kimchi.toString(); // kimchi: 한국의 음식,고춧가루로 배추를 버무려 숙성 및 발효시킨 음식
tang.toString() // 연근 갈비탕: 중국의 음식
sushi.updateDef("일본의 음식", "밥을 뭉쳐놓고 그 위에 재료를 얹어낸 음식");
sushi.toString(); // 스시: 밥을 뭉쳐놓고 그 위에 재료를 얹어낸 음식
const dict = new Dict();
dict.add(kimchi);
dict.add(tang);
dict.add(sushi);
dict.all()
// kimchi: 한국의 음식,고춧가루로 배추를 버무려 숙성 및 발효시킨 음식
// 연근 갈비탕: 중국의 음식
// 스시: 밥을 뭉쳐놓고 그 위에 재료를 얹어낸 음식
dict.find("kimchi");
// (2) ['한국의 음식', '고춧가루로 배추를 버무려 숙성 및 발효시킨 음식']
dict.size()
// 3
dict.update("kimchi", "김치")
dict.all()
// 연근 갈비탕: 중국의 음식
// 스시: 밥을 뭉쳐놓고 그 위에 재료를 얹어낸 음식
// 김치: 한국의 음식,고춧가루로 배추를 버무려 숙성 및 발효시킨 음식
dict.rmv("연근 갈비탕");
dict.all()
// 스시: 밥을 뭉쳐놓고 그 위에 재료를 얹어낸 음식
// 김치: 한국의 음식,고춧가루로 배추를 버무려 숙성 및 발효시킨 음식

 

 

interface

 

interface와 type의 차이

 

  • 둘은 타입스크립트에서 오브젝트의 구조를 알려주는 같은 작업을 수행할 수 있지만 다른 기능 들이 있음
  • 추상클래스는 유용한데, 다른 클래스가 가져야 할  property랑 메소드를 명시할 수 있도록 도와줌
  • 인터페이스는 추상클래스와 비슷한 보호를 제공하지만, 인터페이스는 자바스크립트 파일에서 보이지 않음
    파일 크기자 좀 더 커지고, 추가 클래스가 만들어짐
  • 만약 추상클래스를 다른클래스들이 특정 모양을 따르도록 하기 위한 용도로 쓴다면 인터페이스를 쓰는 것이 나음
  • 첫번 째, 기억할 것. 타입을 쓰고 싶다면 type 키워드를 쓰기
    한 가지 옵션은 오브젝트의 모양을 설명하는 것이고 다른 옵션은 alias를 만드는 것, 그리고 또 다른 타입을 특정된 값으로 만드는 거

 

 

abstract class User { //이 추상클래슨 두 개의 메소드를 가지고 있음, 자바스크립트에서 보임
    constructor (
        protected firstName: string,
        protected lastName: string
    ) {}
    abstract sayHi(name:string): string
    abstract fullName():string
}

//상속받은 클래스가 어떻게 동작해야할 지 일러주기 위해서 추상클래스를 사용함
class Player extends User {
    fullName() {
        return `${this.firstName} ${this.lastName}`
    }
    sayHi(name:string) {
        return `Hello ${name}. My name is ${this.fullName}`
    } 
}

//파일 사이즈를 줄임 //자바스크립트 코드로 컵파일 되지 앉음(자바스크리브에서 안 보임)
interface User2 { //인터페이스는 오브젝트나 클래스의 모양을 묘사하도록 해줌
    firstName: string,
    lastName: string,
    sayHi(name:string): string
    fullName():string
}
interface Human {
    health:number
}

class Player2 implements User2, Human {
    constructor (
        public firstName: string,
        public lastName: string,
        public health: number
    ) {}
    fullName() {
        return `${this.firstName} ${this.lastName}`
    }
    sayHi(name:string) {
        return `Hello ${name}. My name is ${this.fullName}`
    } 
}

function makeUser(user: User2) {
    return "Hi"
}
makeUser({
    firstName: "nico",
    lastName: "las",
    fullName: () => "xx",
    sayHi: (name) => "string"
})

 

 

같은 기능을 추상클래스와 interface로 만드려면~

type PlayerA = {
    name: string
}
type PlayerAA = PlayerA & {
    lastName: string
}
//type PlayerAA는 이미 정의되어서 이렇게 하면 안 됌
// type PlayerAA = PlayerA & {
//     health: number
// } 

const playerA: PlayerAA = {
    name:"nico",
    lastName: "las"
}

////
interface PlayerB {
    name:string
}
interface PlayerBB extends PlayerB {
    lastName:string
}
interface PlayerBB {
    health:number
}
const playerB: PlayerBB = {
    name:"nico",
    lastName:"las",
    health: 10
}

+ Recent posts