class Player {
constructor(
private firstName: string,
private lastName: string,
public nickname: string
) {}
}
const nico = new Player("Nico", "las", "니코");
nico.firstName; // private
//Property 'firstName' is private and only accessible within class 'Player'.
abstract class 는 다른 class가 상속받을 수 있는 class이다.
그러나 abstract class는 거기에서 새로운 instance를 만들 수는 없다.
abstract class User {
constructor(
private firstName: string,
private lastName: string,
public nickname: string
) {}
}
//extend를 사용하여 상속받는다.
class Player extends User {
}
const nico_wrong = new User("Nico", "las", "니코");
//Cannot create an instance of an abstract class.
const nico = new Player("Nico", "las", "니코");
nico.nickname; // public
class 안에 존재하는 함수를 method라고 부른다.
abstract class User {
constructor(
private firstName: string,
private lastName: string,
public nickname: string
) {}
getFullName(): string {
return `${this.firstName} ${this.lastName}`;
}
class Player extends User {
}
const nico = new Player("Nico", "las", "니코");
nico.getFullName(); // public
만약 private getFullName(): string
이라면 nico.getFullName();
은 작동하지 않는다.
메소드도 추상화시킬 수 있다. abstract class 안에서 method 를 완전히 구현하는 대신에 call signature만 적어주는 방법으로 가능하다.
abstract class를 상속받은 class에서 method를 구현해야 한다.
abstract class User {
constructor(
private firstName: string,
private lastName: string,
private nickname: string
) {}
abstract getNickName(): void;
getFullName(){
return `${this.firstName} ${this.lastName}`;
}
class Player extends User {
getNickName(): void {
console.log(this.nickname)
//Property 'nickname' is private and only accessible within class 'User'.
}
}
abstract class User {
constructor(
protected firstName: string,
protected lastName: string,
protected nickname: string
) {}
abstract getNickName(): void;
getFullName(){
return `${this.firstName} ${this.lastName}`;
}
class Player extends User {
getNickName(): void {
console.log(this.nickname) //protected
//abstract class를 상속받은 class 내부이기 때문에 접근할 수 있다.
}
}
type Words = {
[key: string]: string
}
//[key: string]
//제한된 양의 property 또는 key를 가지는 타입을 정의하는 방법
//property의 이름은 모르지만 type은 알고 있을 경우 사용
class Dict {
private words: Words
constructor() {
this.words = {}
//property가 constructor로부터 바로 초기화 되지 않는 경우
}
add (word: Word) {
//class를 type처럼 사용할 수 있다.
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")
class Word {
constructor(
public readonly term: string,
public readonly def: string
) {}
}
const kimchi = new Word('kimchi', '한국의 음식')
kimchi.def = '맛있다.'
//cannot assign to 'def' because it is a read-only property
//public 이기 때문에 외부에서도 볼 수는 있지만 값을 바꿀수는 없게한다.
class Dict {
private words: Words
constructor() {
this.words = {}
//property가 constructor로부터 바로 초기화 되지 않는 경우
}
add (word: Word) {
//class를 type처럼 사용할 수 있다.
if (this.words[word.term] === undefined) {
this.words[word.term] = word.def;
}
}
def (term: string) {
return this.words[term]
}
static hello() {
return "hello"
}
}
...
Dict.hello()
//이런식으로 사용할 수 있다.
//타입의 alias를 지정
type Nickname = string
type Health = number
type Friends = Array<string>
//타입이 특정 값만을 갖도록 지정할 수 있음
type Team = "red" | "blue" | "yellow"
//object의 형태와 타입 지정
type Player = {
nickname: string,
team: Team,
healthBar: number,
}
//미리 지정해둔 형태의 타입을 사용
const nico: Player = {
nickname: 'Nico',
team: "red",
healthBar: 10
}
interface Player {
nickname: string,
team: Team,
healthBar: number,
}
//object의 형태를 지정하는 용도로만 사용되는 것이 interface
//interface
interface User {
name: string;
}
interface Player extends User {
}
const nico : Player = {
name: "Nico"
}
//type
type User = {
name: string;
}
type Player = User & {
}
const nico : Player = {
name: "Nico"
}
interface User {
name: string;
}
interface User {
lastName: string;
}
interface User {
health : number;
}
const nico: User = {
name: 'Nico',
lastName: 'Gonzalez',
health: 100
}
//이런식으로 같은 이름 User를 가진 interface를 여러번 만들어서
//이를 활용할 때 하나로 합칠 수 있다.
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()}`;
}
}
이런식으로 코드를 작성했을 때, JS로 컴파일 된 코드에는
class User
가 추상화 되지 않고 사용되지도 않는 단순한 class로 남아있게 된다.
한편 interface는 JS로 컴파일 될 때 사라진다.
TypeScript에서만 존재하는 문법이기 때문이다. 그래서 더 가벼워진다. (파일 크기를 줄일 수 있다.)
interface User {
firstName: string,
lastName: string,
sayHi(name: string): string,
fullName(): string
}
interface Human {
health: number
}
//extends 대신 implements
class Player implements User {
constructor(
//interface를 상속할 땐 property를 private, protect로 만들 수 없다.
//public이 되어야 한다.
public firstName: string,
public lastName: string
public health: number
) {}
fullName(): string {
return `${this.firstName} ${this.lastName}`;
}
sayHi(name: string): string {
return `Hi ${name}, my name is ${this.fullName()}`;
}
}
//인터페이스도 타입으로 지정할 수 있다.
function makeUser(user: User) {
return user.sayHi('John');
}
//type 으로 object 형상 정의 가능
type PlayerA = {
name: string;
}
//type을 상속하여 형상 정의 가능
type PlayerAA = PlayerA & {
lastName: string;
}
//type을 이용하여 object 생성
const player: PlayerAA = {
name: 'John',
lastName: 'Doe'
}
/////
//interface로 object 형상 정의 가능
interface PlayerB {
name: string;
}
//interface를 상속하여 형상 정의 가능
interface PlayerBB extends PlayerB {
lastName: string;
}
//interface를 한번 더 사용하여 property 추가 가능
//type으로는 불가능하다.
interface PlayerBB {
health: number;
}
//interface를 이용하여 object 생성
const PlayerB: PlayerBB = {
name: 'John',
lastName: 'Doe',
health: 100
}
type PlayerA = {
firstName: string;
}
interface PlayerB {
firstName: string;
}
class UserA implements PlayerA {
constructor(
public firstName: string
) {}
}
class UserB implements PlayerB {
constructor(
public firstName: string
) {}
}
TS 커뮤니티에서는 class 나 object 의 모양을 정의해야 할 땐
interface를 사용하는 것을 권장한다.
//3. interface SStorage는 받은 제네릭 타입 <T>를 이용해
//value의 타입을 제네릭으로 지정
interface SStorage<T> {
[key: string]: T
}
//1. class LocalStorage를 초기화할 때 제네릭 타입 <T>를 받을 것이라고 선언
class LocalStorage<T> {
//2. 앞서 선언한 제네릭 타입 <T>를 interface SStorage<T>에 상속
private storage: SStorage<T> ={}
set(key: string, value: T) {
if (this.storage[key] !== undefined) {
throw new Error('already exist');
}
this.storage[key] = value;
}
remove(key: string) {
delete this.storage[key]
}
get(key: string): T {
return this.storage[key]
}
clear() {
this.storage = {}
}
}
const stringsStorage = new LocalStorage<string>();
stringsStorage.get('ket');
//call signature: (key: string): string
//call signature 선언은 제네릭이었지만 선언을 읽고 자동으로 string으로 바뀌었다.
stringsStorage.set('name', 'Lee');
//call signature: (key: string, value: string): void
const booleanStorage = new LocalStorage<boolean>();
booleanStorage.get('x');
//call signature: (key: string): boolean
//마찬가지로 제네릭 -> boolean으로 변경됨
booleanStorage.set('isPerson', true);
//call signature: (key: string, value: boolean): void