TypeScript - basic

beomjin_97·2022년 1월 28일
1

typescript

목록 보기
1/7

0. typescript

  • 타입스크립트는 type을 추가함으로써 자바스크립트를 확장한다.
    (Dynamic type -> Static type)
  • 타입스크립트는 당신이 코드를 run하기 전에 error를 캐치하고 fixes를 제공하여 시간을 절약한다.
  • 타입스크립트는 complied language 이다.
  • 완전히 오픈 소스 이다.


1. 설치 및 실행

1.1 글로벌 설치 및 실행

> npm i typescript -g -------------------(global 설치)
> tsc --init -------------------------------(tsconfig.json 파일 생성)
> tsc -w ----------------------------------(watch 모드)

1.2 프로젝트 단위 설치 및 실행 (권장)

> npm init -y ----------------------------(npm 프로젝트 시작)
> npm i typescript -D -------------------(원하는 버전, devDependency로 설치)
> npx tsc --init --------------------------(tsconfig.json 파일 생성)
> npx tsc -w ---------------------------- (watch 모드)

npx 대신 package.json의 script를 추가하여 사용.

 // package.json

{
"scripts": {
  "build": "tsc"
  "build:watch": "tsc -w" 	 
}

> npm run build
> npm run build:watch



2. Basic Type

2.1 primitive type

Object 또는 reference 형태가 아닌 실제 value를 저장하는 data type

  • boolean
  • number
  • string
  • symbol
  • null
  • undefined

literal 값으로 primitive type의 sub type을 나타낼 수 있다.
또는 래퍼 객체로 만들 수 있다.
그러나 타입스크립트에서는 래퍼 객체를 type으로 작성하는 것을 명시적으로 반대한다.

function reverse(s: String): String {
  return s.split("").reverse().join("");
}

reverse("hello typescript")

String -> string 으로 바꾸어 작성

function reverse(s: string): string {
  return s.split("").reverse().join("");
}

reverse("hello typescript")

2.2 boolean

let isDone: boolean = false;
isDone = true;
console.log(typeof isDone); // 'boolean'

2.3 number

const decimal: number = 9;
const hex: number = 0xf00d;
const binary: number = 0b1011;
const octal: number = 0o542;
const notANumber: number = NaN;
const underscoreNum: number = 1_000_000;

2.4 string

const myName: string = 'beomjin_97';
myName = 'lemon';

const fullName: string = 'Mark Lee';
const age: number = 20;
const sentence: string = `Hello, My name is ${fullname}.
I'll be ${age + 1} years old`

2.5 symbol

new Symbol로 사용할 수 없다.
Symbol을 함수로 사용해서 symbol타입을 생성한다. Symbol('foo')

const sym = Symbol();
const obj = {
  [sym]: "value"
};

obj[sym]  //으로 접근

2.6 null & undefined

let u: undefined = undefined;
let n: null = null;

default 설정에 따르면 undefiend와 null은 모든 타입의 subtype이다.
그러나 '--strictNullCheck' 에 true를 할당하면 null과 undefined는 자기 자신의 타입에만 할당할 수 있다. (단, undefined는 void 타입에 할당 가능)

// tsconfig.json
{
  "compilerOptions": {
    "strictNullChecks": true,     
  }
}

이때, 다른 타입에 null과 undefined를 명시적으로 할당할 수 있게 하려면, union type을 사용한다.

let name: string | null = null;
union = 'Mark';

2.7 object

non-primitive type을 나타내고 싶을 때 type으로 object를 할당한다.

declare function create(o: object | null): void;

create({prop: 1});
create(null);
create('string');  //Error
create(true);  //Error

2.8 Array

let list1: number[] = [1, 2, 3];    // 권장되는 방식
let list2: Array<number> = [1, 2, 3];

let list3: (number | string)[] = [1, 2, 3, "a"];

2.9 tuple

let tup: [string, number];
tup = ['seohwa', 24]
tup = [24, 'seohwa']   //Error
tup[2] = 'el'  //Error, length도 타입에 맞게 맞춰야한다.

const person: [string, number] = ['seohwa', 24];
const [first, second] = person;
// first는 string type
// second는 number type

2.10 any

any는 전체적인 type system에 문제를 일으킬 가능성(compile time에 type check가 정상적으로 이뤄지지 않음)이 있기 때문에 any 타입에 대해 정확히 알고 제한된 상황에서만 사용해야 한다.

any는 어떤 타입을 가지고 있는지 알 수 없음(어느 타입이어도 상관없음)을 나타내고 어느것도 할 수 없다는 것이 아닌 어떤 것이든 할 수 있다는 의미를 나타낸다.

let comp: any = 25;
comp.length  //compile time에 error가 발생하지 않음  

let comp: number = 25;
comp.length  //error 발생

any는 object를 통해 지속적으로 전파된다.
따라서 타입 안정성을 잃지 않기 위해 필요하지 않은 경우에는 any를 사용하는 것을 지양해야 한다.

let looselyTyped: any = {};
let d = looselyTyped.a.b.c.d; // type적으로 에러가 나지 않음


function leakingAny(obj: any) {
  const a: number = obj.num;  //any 누수를 막는 지점
  const b = a + 1;  
  retrun b;
}
const c = leakingAny({num: 0});
c.indexOf("0");  // type error 발생

2.11 unknown

any보다 type-safe한 타입으로 모르는 변수의 타입의 변수를 묘사할 때 any 대신 사용한다.
compiler가 타입을 추론할 수 있게 타입의 유형을 좁히거나 확정시키지 않으면 type error를 발생시킨다.

declare const maybe: unknown;
const aNumber: number = maybe;  // type error

// type guard
if (maybe === true) {
  const aBoolean: boolean = maybe;  // if문 안의 maybe는 boolean 타입
  const aString: string = maybe; // type error
}

if (typeof maybe === 'string') {
  const aString: string = maybe; // if문 안의 maybe는 string 타입
  const aBoolean: boolean = maybe; // type error

2.12 never

일반적으로 return에 사용되는 type.

function error(message: string): never {
  throw new Error(message);
}

function fail(): never {
  return error("failed");
}

function infiniteLoop(): never {
  while(true) {}
}

never 타입은 모든 타입의 subtype이며, 모든 타입에 할당 할 수 있다.
그러나, never에는 그 어떤 것도 할당할 수 없다. (any도 할당할 수 없다.)

let a: string = 'hello'
if (typeof a !== 'string') {
  a;  // never type
}

declare const b: string | number;
if (typeof a !== 'string') {
  a; // number type
}

type Indexable<T> = T extends string ? T & { [index: string]: any} : never;
const c:Indexable<{}> = ''; // 잘못된 타입을 입력하면 never가 되게한다.

2.13 void

undefined와 같은 의미(상태).
함수의 return값을 가지고 무언가를 하지 않겠다는 명시적 표시

function returnVoid(message: string) {
  console.log(message);
  return ;
}


function returnVoid(message: string): void {
  console.log(message);
  return undefined;
}

// void, undefined가 있든 없든 두 함수는 동일하게 작동한다.

const res = returnVoid("리턴");  //void type


3. Type System

3.1 타입스크립트의 타입시스템

  • 타입을 명시적으로 지정할 수 있다.
  • 타입을 명시적으로 지정하지 않으면, 컴파일러가 자동으로 타입을 추론한다.
  • Sturctural type system : 이름이 달라도 구조가 같으면 같은 타입으로 취급한다.

3.2 타입호환성

3.2.1 서브 타입과 슈퍼 타입

서브타입은 슈퍼타입에 할당할 수 있지만 슈퍼타입은 서브타입에 할당 할 수 없다.
(함수의 포함 관계 또는 정수, 유리수, 실수의 관계를 생각하자.)

let sub1: 1 = 1;
let sup1: number = sub1;
sub1 = sup1 // error

let sub2: number[] = [1];
let sup2: object = sub2;
sub2 = sup2; //error

let sub3: [number, number] = [1, 2];
let sup3: number[] = sub3;
sub3 = sup3; // error

let sub4: number = 1;
let sup4: any = sub4;
sub4 = sup4  //error가 발생하지 않는다. 

let sub5: never = 0 as never;  //일반적인 사용방법은 아니다
let sup5: number = sub5;
sub5 = sup5;  //error

class Animal {}
class Dog extends Animal {
  eat() {}
}
let sub6: Dog = new Dog();
let sup6: Animal = sub6;
sub6 = sup6  //error

3.2.2 같거나 서브 타입인 경우 할당이 가능(공변)

let sub7: string = '';
let sup7: string | number = sub7;

let sub8: {a: string; b: number } = {a: '', b: 1};
let sup8: {a: string | number; b: number } = sub8;

let sub9: Array<{ a: string; b: number }> = [{ a: '', b: 1 }];
let sup9: Array<{ a: string | number; b: number }> = sub9; 

3.2.3 함수의 매개변수 타입만 같거나 슈퍼타입인 경우 할당 가능(반병)

class Person {}
class Developer extends Person {
  coding() {}
}
class StartupDeveloper extends Developer {
  burning() {}
}

function tellme(f: (d: Developer) => Developer) {}

tellme(function dToD(d: Developer): Developer {
  return new Developer();
});

tellme(function pToD(d: Person): Developer {
  return new Developer(); 
})  // Person의 모든것이 Developer를 범위를 벗어나지 않기 때문에 할당 가능

tellme(function sToD(d: StartupDeveloper): Developer {
  return new Developer();
})  // StartupDeveloper의 일부가 Developer의 범위를 벗어나기 때문에 할당 불가능

3.3 Type Alias

  • Interface와 유사하다.
  • Primitive, Union Type, Tuple, Function 등 기타 직접 작성해야 하는 타입을 다른 이름으로 지정할 수 있다.
  • 타입을 만드는 것이 아닌 만들어진 타입의 reference로 사용한다.

3.3.1 Aliasing Union Type

type StringOrNumber = string | number;
let another: StringOrNumber = 0;
another = 'A';

3.3.2 Aliasing Tuple

type PersonTuple = [string, number];
let another: PersonTuple = ['A', 0];

3.3.3 Aliasing Function

type EatType = (food: string) => void;


4. Typescript Complier

tsconfing.json의 최상위 Properties

  • compileOnSave
  • extends
  • compileOptions
  • files
  • include
  • exclude
  • references
  • typeAcquisition
  • tsNode

4.1 complieOnSave

{
  "compileOnSave" : true,
}
  • save시에 자동으로 compile 하기위한 옵션
  • defalut false
  • visual Studio 2015 & TypeScript 1.8.4 이상

4.2 extens

{
  "extends" : [],
}

4.3 files

{
  "files" : ["./src/main.ts",],
}
  • 어떤 파일을 compile 할 것인지 경로를 작성한다.
  • exclude 보다 우선적으로 적용된다.

4.4 exclude

{
  "exlude" : ["/dist"],
}
  • 어떤 파일을 compile 대상에서 제외할 것인지 glob pattern으로 작성한다.
  • include 보다 우선적으로 적용되지만 files보다는 후순위로 적용된다.
  • defalut: (node_modules, bower_components, jspm_packages, <outDir> 제외
  • <outDir> 은 항상 제외한다.
  • Typescript 2.0 이상

4.5 include

{
  "include" : [".ts", ".tsx"],
}
  • 어떤 파일을 compile 할 것인지 glob pattern으로 작성한다.
  • exclude보다 후순위로 적용된다.
  • * 사용시 .ts / .tsx / .d.ts 만 포함한다.
  • Typescript 2.0 이상

4.5.1 files, exclude, include

files, exclude, include 모두 설정이 없으면, 모든 파일을 compile한다.

4.6 complierOptions

4.6.1 typeRoots & types

{
  "compilerOptions": {
    "typeroot": ["./path",],
    "types": ["package name",]
  }
}

자바스크립트 기반의 외부 모듈 또는 라이브러리의 type definition 시스템을 위한 option

  • 아무 설정도 하지 않으면 node_modules/@types 라는 모든 경로를 찾아서 사용한다.
  • typeRoots : 배열안의 경로들 아래서만 찾아서 사용
  • types: 배열안의 모듈 혹은 node_modules/@types/ 안의 모듈 이름에서 찾아서 사용, 빈배열은 이 시스템을 사용하지 않겠다는 의미
  • 일반적으로 typeRoots와 types를 같이 사용하지 않는다.

4.6.2 target & lib

{
  "compilerOptions": {
    "target": "es6",
    "lib": ["dom","es6","dom.iterable","scripthost"]
  }
}

target

  • 빌드에 의한 결과물의 자바스크립트 버전
  • default: es3

lib

  • 컴파일에 포함될 라이브러리 파일 목록, (기본 type definition)
  • 지정하지 않은 경우 : target에 맞춰 defalut값을 지정한다.
  • 지정하면 지정된 배열의 값으로만 라이브러리를 사용한다.

https://kangax.github.io/compat-table/es6/

4.6.3 outFile, outDir & rootDir

{
  "compilerOptions": {
    "outFile": ,
    "outDir": "./dist",
    "rootDir": "./src"
  }
}

outFile

rootDir : 입력 파일의 루트 디렉토리(출력 구조)를 지정한다.
outDir : rootDir에서 지정한 출력 구조를 해당 디렉토리로 리다이렉트한다.

4.6.5 strict

{
  "compilerOptions": {
    "strict":true,
    //"noImplicitAny":true,
    //"noImplicitThis":true,
    //"strictNullChecks":true,
    //"strictFunctionTypes":true,
    //"strictPropertyInitialization":true,
    //"strictBindCallApply":true,
    //"alwaysStrict":true,
    //"noImplicitReturns":true
  }
}

4.6.5.1 noImplicitAny

타입이 명시적으로 지정되어 있지 않은 경우, 컴파일러가 추론중 타입이 'any'라고 판단하게 되면 (또는 타입 추론에 실패하게 되면), 컴파일 에러를 발생시킨다.
'any'가 정말 맞는지 확인하고 맞다면 명시적으로 'any'라고 지정해야한다.

function f1 (a) {  //error
  return a * 38; 
} 

4.6.5.2 noImplicitThis

this 표현식에 타입이 명시적으로 지정되어 있지 않은 경우 컴파일 에러를 발생시킨다.
이를 해결하기 위해서는 첫번째 매개변수에 this를 지정하고 type을 지정해야한다.

function noInpliitThisFunc (this: any, name: string, age: number) {
  this.name = name;
  this.age = age;
  
  return this;
} 

this에 any타입을 할당하는 것은 합리적일 수도 있다.
Class에서는 this를 사용하면서 noImplicitThis 에러가 발생하지 않는다.

4.6.5.3 strictNullCHecks

  • 이 옵션을 적용하지 않으면 모든 타입은 null, undefined 값을 가질 수 있게된다.
  • 적용하면 모든 타입은 null, undefined 값을 가질 수 없게 되고, 가지려면 union type를 이용해서 직접 명시해야 한다.
  • 이후 조건들을 이용해 null, undefined 값을 분기처리하고 리턴값에 정확한 타입을 지정하려는 방식으로 코드를 작성해야 한다.

4.6.5.4 strictFuctionTypes

  • 인자 타입은 반공변적
  • 반환 타입은 공변적

4.6.5.5 strictPropertyInitialization

정의되지 않은 Class의 속성이 constuctor에서 초기화되었는지 확인한다.

Class Person {
  private _name: string; //error
  private _age: number;  //error
  
  //constructor(name: string, age: number) {
  //  this._name = name;
  //  this._age = age;
}

비동기 처리를 위해서 constructor 대신 다른 함수에서 초기화하는 경우, !로 에러를 무시할 수 있다.

Class Person {
  private _name!: string; 
  private _age!: number;  
  
  public async initialize (name: string, age: number) {
  this._name = name;
  this._age = age;
}

4.6.5.6 strictBindCallApply

bind, call, apply에 대한 더 엄격한 검사 수행

4.6.5.7 alwaysStrict

각 소스 파일에 대해 strict mode로 코드를 분석하고, 컴파일된 자바스크립트 파일에 "use strict"를 추가한다.

4.6.5.8 noImplicitReturns

함수 내에서 모든 코드(경로)가 값을 리턴하지 않으면 컴파일 에러를 발생시킨다.

function f4 (a: number): number {
  if (a > 0) {
    return a * 38;
  } //else {
  // return a * 23;
}

console.log(f4(5));
console.log(f4(-5) + 5)  //error

5. Interfaces

5.1 interface

반복되는 타입을 하나의 인터페이스로 지정

interface Person1 {
  name: string;
  age: number;
}

function hello1(person: Person1): void {
  console.log(`h1 ${person.name}`);
}

const p1: Person1 = {
  name: 'mary`,
  age: 40
};    

hello1(p1);

5.2 optional property

interface Person2 {
  name: string;
  age?: number;   // age는 없어도 된다. 
}

function hello2(person: Person2): void {
  console.log(`h1 ${person.name}`);
}

hello2({ name: 'mary',age: 39 })
hello2({ name: 'mary' })  // compile error가 발생하지 않는다.

5.2.1 indexable type

객체에 추가될 key와 value의 타입을 지정한다.

interface Person3 {
  name: string;
  age?: number;
  [index: string]: any;
}

function hello3(person: Person2): void {
  console.log(`h1 ${person.name}`);
}

const p31: Person3 = {
  name: 'mark',
  age: 39,    // ?와 마찬가지로 indexable type도 없어도 상관없다.
};

const p32: Person3 = {
  name: 'Anna',
  sisters: ["sung","chan"]
};

const p33: Person3 = {
  name: 'mary',
  father: p31,
  mother: p32,   // 타입만 지키면 개수는 상관없다.
};

hello3(p31);
hello3(p32);
hello3(p33);

5.3 function in interface

interface의 속의 함수의 타입을 지정.

interface Person4 {
  name: string;
  age: number;
  hello(): void;
}

const p41: Person4 = {
  name: 'Mark',
  age: 39,
  hello: function(): void {
    console.log(`hi ${this.name}`)
  }
};

const p42: Person4 = {
  name: 'Mark',
  age: 39,
  hello(): void {
    console.log(`hi ${this.name}`)
  }
};

const p43: Person4 = {
  name: 'Mark',
  age: 39,
  hello: (): void => {
    //console.log(`hi ${this.name}`)  arrow function 내에서는 this= global.this
  }
};

5.4 class implements interface

interface로 부터 class를 만들어낸다.

interface IPerson1 {
  name: string;
  age?: number;
  hello(): void;
}

class Person implements IPerson1 {
  name: string;
  age?: number | undefined;
  
  constructor(name: string) {
    this.name = name;
  
  hello(): void {
    //throw new Error("Method not implemented.");
    console.log(`h1 ${this.name});
  }
}

const person: IPerson1 = new Person('Mark');
person.hello();

5.5 inheritance

interface은 상속을 내려주고 상속 받을 수 있다.

interface IPerson2 {
  name: string;
  age?: number;
}

interface IKorean extends IPerson2 {
  city: string;
}

const k: IKorean = {
  name: 'sujin',
  city: 'seoul',
};

5.6 function interface

함수의 타입을 인터페이스로 표현

interface HelloPerson {
  (name: string, age?: number): void;
}

const helloPerson: HelloPerson = function (name: string) {
  console.log(`hi ${name}`);
}

helloPerson("Mark", 39)

5.7 readonly property

interface Person8 {
  name: string;
  age: number;
  readonly gender: string;
}

const p81: Person8 = {
  name: 'Mark',
  gender: 'male'
}

p81.gender = 'female'  // compile error: 읽기전용

5.8 Interface와 Alias의 차이점

type의 이유와 목적이 명확하면 Interface, 그저 별칭이 필요한 경우는 Alias.
물론 기술적인 부분도 차이점이 존재한다.

5.8.1 function

// type alias 
type EatType = (food: string) => void;

// interface
interface iEat {
  (food: string): void 
}

5.8.2 array

// type alias 
type PersonList = string[];

// interface
interface iPersonList {
  [index: number]: string; 
}

5.8.3 intersection

interface ErrorHandling {
  success: boolean;
  error?: { message: string }
}

interface ArtistsData {
  artist: { name: string } [];
}

//type alias
type ArtistsResponseType = ArtistsData & ErrorHandling;

//interface
interface IArtistResponse extends ArtistsData, ErrorHandling {}  // 다중 상속으로 intersection 구현

5.8.4 union types

interface Bird {
  fly(): void;
  layEggs(): void;
}

interface Fish {
  swin(): void;
  layEgg(): void;
}

type PetType = Bird | Fish;

// interface로는 구현하기 어려움

5.8.5 Declaration Merging - interface

같은 이름의 interface를 작성해도 merging 된다.
type alias에서는 같은 이름을 사용할 수 없다.

interface MergingInterface {
  a: string;
}

interface MergingInterface {
  b: string;
}

let mi: MergingInterface;


6. class

클래스는 ES6 이상에서도 사용하는 기능이다. 타입스크립트는 이를 보강하여 사용자가 만드는 타입의 하나로 클래스를 사용한다.

class Person {
  name 
  constructor (name: string) {
    this.name = name
  }
}

const person = new Person('beomjin')
console.log(person.name)

6.1 initialize

class Person {
  name: string = 'mark'; // constructor없이 직접 할당
  age: number;           // 초기화하지 않으면 typeError
}

런타임 상에 값을 의도적으로 할당하는 경우 !를 붙여준다.

class Person {
  name: string = 'mark'; 
  age!: number;           
}

const p1: Person = new Person();
p1.age = 39              // 런타임에 할당
console.log(p1.age)

값을 선택적으로 할당하고 싶으면 ?를 붙여준다.

class Person {
  name: string = 'mark'; 
  age: number;   
  constructor(age?) {
    if (age === undefined) {
      age: number = 20
    } else {
      this.age = age
    }
  }
}

const p1: Person = new Person(43);
const p2: Person = new Person();    //error가 발생하지 않는다.

6.2 access modifiers

접근 제어자에는 public, private, protected 가 있다.

  • private: class 내부, 외부 모두 접근할 수 있다.
  • private: class 외부에서 접근할 수 없다.
  • protected: 외부에서 접근할 수 없지만 상속 관계에서는 접근할 수 있다.
class Person {
  public name: string = 'mark'; 
  private _age: number;   
  public constructor(age?) {
    if (age === undefined) {
      age: number = 20
    } else {
      this.age = age
    }
  }
}


const p1: Person = new Person(43);    //접근 가능
console.log(p1.name);  // 접근 가능
console.log(p1._age);  // 접근 불가  

6.3 constructor를 통한 initialization

클래스 필드 선언과 constructor 내부에 this 바인딩을 통한 값을 할당하는 코드 대신에
접근제어자를 통해 아래와 같이 작성할 수 있다.

class Person {
  name: string;
  age: number;
  constructor(name: string, age: number) {
    this.name = name;
    this.age = age;
  }
}

대신에

class Person {
  public constructor(public name: string, public age: number) {}
}

6.4 getter & setter

타입스크립트에서도 접근자 프로퍼티를 사용할 수 있다.

class Person {
  public constructor(private _name: string, private _age: number) {}
  
  get fullName () {
    return this._name + "Lee"
  }
  
  set fullName(name: string) {
    this._name = n;
  }
}

const p1: Person = new Person("Mark", 40);
console.log(p1.fullName); // getter
p1.fullName = "hejin";  //setter

6.5 readonly propery

class Person {
  public readonly name: string = 'Mark';
  private readonly country: string = 'korea';

  public constructor( private age: number) {
  }
  hello() {
    this.country = 'japan'    // Error: read-only property
  }
}

const p1: Person = new Person(30)
console.log(p1.country)  // Error: private property

6.6 index signature

class Students {
  [index: string]: "male" | "female";
  jin: "male" = "male"
}

const a = new Student();
a.mark = "male"
a.jade = "male"

const b = new Student();
b.chloe = "female"
b.alex = "male"
b.anna = "female"

6.7 singletons

단 하나의 객체만 만드는 class

class ClassName {
  private static instance: null | ClassName = null
  public static getInstance(): ClassName {
    if (ClassName.instance === null) {
      ClassName.instance = new ClassName();
    } 

    return ClassName.instance
  }
  private constructor() {} //외부에서의 생성을 막는다.
}

const a = ClassName.getInstance();
const b = ClassName.getInstance();

console.log(a === b)  // true

6.8 Inheritance

ref. class inheritnace in javascript

class Parent {
  constructor(protected _name: string, private _age: number) {}
  
  public print(): void {
    console.log(`이름은 ${this._name}이고, 나이는 ${this._age}이다.`)
  }
}

const p = new Parent('mark', 24)
// p._name  // Error
p.print();

class Child extends Parent {
  //public _name = 'Mark Jr.'  // 부모의 프로퍼티를 오버라이드
  public gender = "male"     // 프로퍼티 추가

  constructor(age: number) {
    super('Mark Jr.',age)
    super.print();
  }

}

const c = new Child(5)

6.9 Astract class

추상 클래스: class의 기능들이 완전하지 않은 채로 class를 정의한다.
이후, 상속을 통해 완전하게 구현한다.

abstract class APerson {
  protected _name: string = "Mark";

  abstract setName(name: string): void
}

new APerson()  // 추상클래스의 인스턴스를 만들 수 없다.

class Person extends APerson {
  setName(name: string): void {
    this._name = name;
  }
}

const p = new Person();
p.setName('envy');


7. Generic

들어오는 타입에 따라 그것의 가장 좁은 타입을 지정하여 함수(또는 class, array등) 내에서 이용할 수 있다.

function helloGeneric<T>(message: T): T {
  return message;
}

console.log(helloGeneric('marin').length)
console.log(helloGeneric(22).length)  //Error

여러개를 지정할 수도 있고 함수를 호출할 때 명시적으로 타입을 지정할 수도 있다.

function helloBasic<T, U>(message: T, comment: U): T {
  return message;
}

helloBasic<string, number>('Mark', 39);  // 타입 지정
helloBasic(36, 39);                      // 타입 추론

7.1 Generic Array & Tuple

함수의 인수로 array를 받을 때, 리턴 타입

function helloArray<T>(message: T[]): T {
  return message[0];
}

helloArray(['Hello', 'World']);
helloArray(['Hello', 5])   // string | number 유니온타입

function helloTuple<T, K>(message: [T, K]): T {
  return message[0]
}

helloTuple(['Hello', 5])  // string으로 정확하게 지정된다.

7.2 Generic Function

함수의 타입만 지정하고 나중에 함수 구현

type HelloFunctionGeneric1 = <T>(message: T) => T

const helloFunction1: HelloFunctionGeneric1 = <T>(message: T): T => {
  return message;
}

interface HelloFunctionGeneric2 {
  <T>(message: T): T;
}

const helloFunction2: HelloFunctionGeneric2 = <T>(message: T): T => {
  return message
}

7.3 Generic class

class Person<T, K> {
  private _name: T;
  private _age: K;
  constructor (name: T, age: K) {
    this._name = name;
    this._age = age;
  }
}

new Person ("Mark", 39);
new Person<string, number>("Mark", "age") // Error
new Person<string, number>("Mark", 20)

7.4 extends

class PersonExtends<T extends string | number> {
  private _name: T;

  constructor(name: T) {
    this._name = name;
  }
}

new PersonExtends("Mark");
new PersonExtends(39);
new PersonExtends(ture); //Error

7.5 keyof & type lookup system

generic을 이용하면 아래와 같이 안전한 타이핑을 할 수 있다.

interface IPerson {
  name: string;
  age: number;
}

const person: IPerson = {
  name: "Mark",
  age: 39,
}

function getProp<T, K extends keyof T>(obj: T, key: K): T[K] {
  return obj[key];
}
getProp(person, 'age')  

function setProp<T, K extends keyof T>(obj: T, key: K, value: T[K]): void {
  obj[key] = value;
}
setProp(person, 'name', 'Anna');
profile
Rather be dead than cool.

0개의 댓글