Typescript 시작


EggheadUse Types Effectively in TypeScript를 공부하며 정리한 내용입니다.


Typescript

환경 세팅

$ npm install -g typescript // npm으로 설치 
$ tsc -v // 버전
$ tsc main.ts // main.ts >> main.js 변환

playground

https://www.typescriptlang.org/play/

Union Types

let thing : string | number | string[] | boolean;

let returnSomthing = (someThing : string | number | string[] | boolean ) => {
  return someThing;
};
type thing = string | number | string[] | boolean;

let returnSomthing = (someThing : thing ) => someThing;
type thing = string | number | string[] | boolean;

let returnSomthing = (someThing : thing ) => {
  if (typeof someThing === "string") ||
      typeof someThing === "number") ||
      typeof someThing === "boolean")) {
        console.log("someThing = ", someThing )
      }
  if (someThing instanceof Array) {
    let joinedThings = "";
    someThing.forEach((thing) => {
        joinedThings += `${thing}`;
      });
      console.log("joinedThings = ", joinedThings);  
  }
};

returnSomthing(123); // someThing = 123
returnSomthing(["a","b","c"]); // joinedThings = abc
type stuff = string | {name:string};
let gimmeStuff = (stuff: stuff) => {
  typeof stuff === "string";
  typeof stuff === "string";    
};  
// error TS2339: Property 'name' does not exist on type 'string | { name: string; }'
// object와 not object가 함께 있으면 에러가 남.
type coolThings = {name: string;} | {id: number;};
let gimmeCoolthings = (thing: coolThings) => {
  if (typeof thing.name === "string") { return thing.name; }
  if (typeof thing.id === "number") { return thing.id; }
};  
// error TS2339: Property 'name' does not exist on type '{ name: string; } | { id: number }'
// error TS2339: Property 'name' does not exist on type '{ name: string; } | { id: number }'
// error TS2339: Property 'id' does not exist on type '{ name: string; } | { id: number }'
// error TS2339: Property 'id' does not exist on type '{ name: string; } | { id: number }'
// not object끼리 있더라도 같은 parameter가 없으면 에러가 남
type stuffAndThings = {cool: string; meh: string;} | {cool: string; lame: string; }
let gimmeStuffAndThings = (sat: stuffandThings) => {
  return sat.cool;
};
// It works! 같은 parameter가 있으면 된다.

string literal type

let unit: string = "아무거나 올 수 있다.";
let miles: "MILES" = "MILES"; // literal type. type과 같은 값, null, undefined 만 가능.
type distanceMetric = "MILES" | "KILOMETERS" | "METERS" | "YARDS" | "FEET" | "INCHES";
function moveCharacter(distance: number, value: distanceMetric) {
  console.log(`You moved ${distance} ${value}`);
   // error TS2345: Argument of type '"dragon"' is not
   // assignable to parameter of type '"MILES" | "KILOMETERS" | ... '
};

interface

let superHero: { secretIdentity: string; superHeroName: string; health: number };
let superVillain: { secretIdentity: string; superHeroName: string; health: number };
interface ComicBookCharacter {
  secretIdentity?: string;  // ? : optional
  alias: string;
  health: number;
}

let superHero: ComicBookCharacter = {
  alias: true,      // Types of property 'alias' are incompatable. Type 'boolean' is not assignable to type 'string'.
  health: 5000      
}
let superVillain: ComicBookCharacter = {
  secretIdentity: "Jack Napier", // Object literal may only specify known
  alias: "Joker",               // properties and 'secretIdentity' does not
  health: 75                    // exist in type 'ComicBookCharacter'
}
function getSecretIdentity(character: ComicBookCharacter) {
  if (character.secretIdentity) {
    console.log(`${character.alias} is ${character.secretIdentity}`);
  } else {
    console.log(`${character.alias} has no secret identity`);
  }
}
getSecretIdentity(superHero);  
interface AttackFunction {
  (opponent: { alias: string; health: number; }, attackWith: number): number; // opponent와 attackWith를 parameter로 받는 function
}

interface KrustyTheClown {
  alias: string;
  health: number;
  inebriationLevel: number;
  attack: AttackFunction;
}

function attackFunc(opponent, attackWith) {
  opponent.health -= attackWith;
  console.log(`${this.alias} attacked ${opponent.alias}, who's health = ${opponent.health}`);
  return opponent.health;
}

let superVillain: ComicBookCharacter = {
  scretIdentity: "Jack Napier",
  alias: "Joker",               
  health: 75,
  insanity: 175,
  attack: attackFunc
}
interface OptionalAttributes {
  strength?: number;
  insanity?: number;
  dexterity?: number;
  healingFactor?: number;
}

interface ComicBookCharacter extends OptionalAttributes { ... }

class

class ComicBookCharacter {
  alias: string;
  health: number;
  strength: number;
  secretIdentity: string;

  attackFunc(opponent, attackWith: number) {
    opponent.health -= attackWith;
    console.log(`${this.alias} attacked ${opponent.alias} who's health = ${opponent.health}`);
  }
}
class ComicBookCharacter {
  alias: string;
  health: number;
  strength: number;
  private secretIdentity: string;

  attackFunc(opponent: Opponent, attackWith: number) { ... }

  getSecretIdentity() {
    console.log(`${this,alias}'s secret identity is $(this.secretIdentity)`);
  }
}
class ComicBookCharacter {
  attackFunc(opponent: Opponent, attackWith: number) { ... }

  getSecretIdentity() { console.log(`${this,alias}'s secret identity is $(this.secretIdentity)`);}

  constructor(public alias: string, public health: number, public strength: number, private secretIdentity: string) {}
}
class ComicBookCharacter {
  static createTeam(teamName: string, members: ComicBookCharacter[]) {
    name: teamName,
    members: members
  }
}

let instanceTeam = new ComicBookCharacter();
instanceTeam.createTeam("oddCouple, [storm, theBlob]); // error!!!!  

let team = ComicBookCharacter.createTeam("oddCouple, [storm, theBlob]);
class ComicBookCharacter (
  constructor{
    public alias: string, public health: number , public strength: number,
    private secretIdentity: string
  ) {}
}

class SuperHero extends ComicBookCharacter {
  traits = ["empathy", "strong moral code"];

  getSecretId() { console.log(this.secretIdentity);  // not working!!!  
  // extends 된 class 에서는 부모 class의 private property에 접근 할 수없다.
  // private은 자신이 속해있는 container에서만 접근 가능하다.
  // 이 경우, secretIdentity 를 protected로 변경해주면 된다.
}

class SuperVillain extends ComicBookCharacter {
  flaws = ["hubris", "always explains evil plan"];
}

let jubilee = new SuperHero("Jubilee", 23, 233, "Jubilation Lee");
let scarletWitch = new SuperVillain("Scarlet Witch", 233, 4444, "Wanda Maximoff");
class ComicBookCharacter (
  constructor{
    public alias: string, public health: number , public strength: number,
    protected secretIdentity: string
  ) {}
}

class SuperVillain extends ComicBookCharacter {
  flaws = ["hubris", "always explains evil plan"];

  constructor(a, b, c, d) {
    super(a, b, c, d);
    console.log('${this.alias} eats kittens!!!');
  }  
}

Type Converting

interface SuperHero {
  powers: string[];
  savesTheDay: () => void;
}

interface BadGuy {
  badDeeds: string[];
  getRandomBadDeed: () => string;
  commitBadDeed: () => void;
}

function saveDayOrBadDeed(something: SuperHero | BadGuy) {
  // if (<SuperHero>something.powers) {} // angle bracket syntax
  if ((something as SuperHero).powers) {
    (something as SuperHero).savesTheDay();
  } else {
    (something as BadGuy).commitBadDeed();
  }
}

saveDayOrBadDeed(dazzler); // Dazzler transduces sonic vibrations into light to save the day!!!
saveDayOrBadDeed(badGuy); // BadGuy farts on old folks

Generics

function pushSomethingIntoCollection(something, collection) {
  collection.push(something);
  console.log(collection);
}

let jeanGrey = { name: "Jean Grey" };
let wolverine = { name: "Wolverine" };

let superHeroes = [jeanGrey];
let powers = ["telekinesis", "esp"];

pushSomethingIntoCollection("cool", superHeroes);
pushSomethingIntoCollection("adamantium claws", []);
// [ { name: 'Jean Grey' }, 'cool' ]
// [ 'adamantium claws' ]
function pushSomethingIntoCollection<T>(something: T, collection: T[]) {
  collection.push(something);
  console.log(collection);
}

let jeanGrey = { name: "Jean Grey" };
let wolverine = { name: "Wolverine" };

let superHeroes = [jeanGrey];
let powers = ["telekinesis", "esp"];

pushSomethingIntoCollection("meh", superHeroes); // error!!!!!!
pushSomethingIntoCollection(jeanGrey, superHeroes); // it works!!
pushSomethingIntoCollection("adamantium claws", []);
interface SuperHero {name: string;}

pushSomethingIntoCollection<SuperHero>("meh", superHeroes); // error!!!
pushSomethingIntoCollection<SuperHero>(jeanGrey, superHeroes); // it works!!  
pushSomethingIntoCollection<string>("adamantium claws", []);

Interface generic constraints

interface Crocodile { personality: string; }
interface Taxes { year: number; }
interface Container<T> { unit: T; }

let crocContainer: Container<Crocodile> = {unit: { personality: "mean"}};
let taxContainer: Container<Taxes> = {unit: {year: 2011}};

interface RedCroc extends Crocodile { color: "red"; }
interface BlueCroc extends Crocodile { color: "blue"; }

interface CrocContainer<T extends Crocodile> { crocUnit: T; }

let blueCrocContainer: CrocContainer<BlueCroc> = {crocUnit: {personality: "cool", color: "blue"}};

Class generic constraints

class ClassyContainer<T extends Crocodile> {
  classyCrocUnit: T;
}

let classyCrocContainer = new ClassyContainer(); // type argument 를 안알려줘도 됨.
classyCrocContainer.classyCrocUnit = {personality: "classy"}; // 하지만  Crocodile을 extends 한 interface들의 property를 사용할 수 없다.

let classyCrocContainer = new ClassyContainer<RedCroc>(); // T을 알려주면
classyCrocContainer.classyCrocUnit = {personality: "classy", color: "red"}; // IDE에서자동완성도 됨.
class CCC<T extends Crocodile> {]
  constructor(public cccUnit: T) {}
}

let ccc = new CCC<BlueCroc>({personality: "ultra classy", color: "blue"});
let ccc2 = new CCC({personality: "ultra classy", color: "blue"}); // it works!!