[TOC]
https://joshua1988.github.io/ts/intro.html
위 링크의 핸드북을 공부하면서, 새로 알게된 것들을 기록해본다. 틀리게 이해한 내용을 보시면 호되게 꾸짖어주세요.
타입명은 소문자로 쓴다
길이와 타입이 정해져 있는 배열.
let arr: [string, number] = ['hi',10];
C#에서 보던 그거. 타입스크립트는 여러모로 C#과 많이 닮아있는 것 같다
enum Avengers { Capt, IronMan, Thor }
let hero: Avengers = Avengers.Capt
인덱스 번호로 접근할 수 있다.
let hero: Avengers = Avengers[0]
또는 인덱스를 멋대로 바꿀 수 있다.
enum Avengers {Capt=2, IronMan, Thor}
let hero: Avengers = Avengers[2]
시작 인덱스만 제공하는 경우, 1씩 증가(auto increment)한다.
함수의 끝에 절대 도달하지 않는다는 타입. void
를 쓰면 안되는건가? 실질적으로 어떤 기능을 한다기보다는 가독성을 위해 의미상 붙여주거나, 에러를 뿜는 컴파일러를 진정시키기(...)위해 사용하는 것 같다.
function neverEnd(): never {
while(true) {
}
}
https://eotkd4791.github.io/typescript/TypeScript06/ 에 따르면
never
는 보통 배열을 잘못 만든 경우에 타입이 never라는 에러가 난다고 함.
함수를 선언할 때, 매개변수와 반환 값에 타입을 추가한다.
function sum(a:number, b:number): number {
return a+b
}
타입스크립트에서는 함수의 인자가 모두 필수값이기 때문에, undefined
또는 null
이라도 무조건 인자로 제공해줘야 한다.
인자가 optional 함을 밝혀주려면 아래와 같이 ?
를 사용한다.
function sum(a: number, b?: number): number {
return a+b
}
sum(10, 20) // 30
sum(10) // 10
매개변수 초기화 (미리 값을 넣어두는 것, 따라서 인자를 제공하지 않아도 작동하며, 제공하면 제공된 값으로 덮어씌어진다.) 는 ES6 문법과 동일하게 한다. 링크의 예제는 초기화가 '100'
으로 되어 있는데, 이건 string
아닌가?
function sum(a:number, b=100):number {
return a+b
}
sum(10, undefined) // 110
sum(10) // 110
sum(10, 20) // 30
this를 먼저 이해하기 위해서, 아래의 링크를 권하고 있다.
근데 this는 클래스에서만 쓰는 거 아닌가? 함수에서 왜 this..?
https://yehudakatz.com/2011/08/11/understanding-javascript-function-invocation-and-this/
자바스크립트의 함수 호출은 다음과 같이 desugaring 할 수 있다.
sugared:
fn(args);
desugared:
fn.call(window [ES5-strict: undefined], ...args)
//window [ES5-strict: undefined] = this
요약하자면, 우리가 fn(args)
형태로 함수를 호출할 때, 사실은 fn 함수의 call 메서드를 호출하는 것이며, 자동으로 this 를 첫번째 인자로 제공한다는 것.
전부 이해하진 못했다, 암튼 this의 타입은 아래와 같이 정해주면 됨.
function 함수명(this: 타입): 타입 {
}
인터페이스라는 게 이놈이 참 골때린 놈인데, 이해한 바에 따르면 타입 검사를 위해 어떤 규칙을 만들어 놓는다는 개념 같다. 즉 객체나 함수가 가져야 할 속성의 타입 같은 것을 '약속'의 형태로 만들어 두는 것. 물론 검사만이 아닌 객체나 함수를 생성할 때도 쓸 수 있다.
타입스크립트는 타입을 엄격하게 써서 코딩을 편리하고 안전하게 하기 위함... 이라는 것이 일반적인 인식인 것 같은데, 나의 취미코딩 레벨에서는 이렇게까지 하면서 굳이 써야 하나? 라는 생각이 들긴 한다. 찾아보니 역시 C#에도 있다. 인터페이스가 대문자 I로 시작하는 이름을 갖는 관습도 역시 C#에서 온 것 같고. C#이 자바스크립트를 침공한(?) 결과가 타입스크립트다. 라는 생각이 확고해졌다
말하자면 아래의 것들을 하나의 변수에 선언해놓고 재사용하는 것이 인터페이스다.
interface personAge {
age: number;
}
function logAge (obj: personAge) {
console.log(obj.age)
}
personAge 인터페이스가 규정하는 조건을 갖춘 객체를 인자로 하는 함수의 선언. age 속성을 가지고 있으므로, 함수 안에서 age 속성을 사용할 수 있다.
interface 인터페이스_이름 {
속성?: 타입;
}
있으면 좋고 없으면 마는 속성이다. 위에서 함수의 optional한 인수를 설정했던 것처럼 ?
를 붙여 만든다.
interface CraftBeer {
name: string;
hop?: number;
}
let myBeer = {name: 'Sapporo'};
function brewBeer(beer: CraftBeer) {
console.log(beer.name);
}
hop
속성이 optional 하기 때문에, myBeer
가 hop
속성을 가지지 않아도 brewBeer
함수의 인수가 될 수 있다.
readonly
키워드를 붙여 만든다.
interface 인터페이스_이름 {
readonly 속성: 타입;
}
interface CraftBeer {
readonly brand: string;
}
let myBeer: CraftBeer = {
brand: 'Belgian Monk'
};
myBeer.brand = 'Korean Carpenter'; // error!
CraftBeer
인터페이스를 충족하는 myBeer
를 생성했으므로, myBeer
의 읽기전용 속성인 brand
를 재정의하려고 할 때 오류를 뿜음.
let arr: ReadonlyArray<number> = [1,2,3];
arr.splice(0,1) // error
arr.push(4); // error
arr[0] = 100; // error
배열을 생성할 때 ReadonlyArray<T>
타입을 사용하면, 읽기전용 배열을 생성할 수 있음.
interface CraftBeer {
brand?: string;
}
function brewBeer(beer: CraftBeer) {
// ..
}
brewBeer({ brandon: 'what' }); // error: Object literal may only specify known properties, but 'brandon' does not exist in type 'CraftBeer'. Did you mean to write 'brand'?
brandon
이 인터페이스에 없기 때문에 에러가 난다. 이런 타입 추론을 무시하려면 아래와 같이 선언.
let myBeer = {brandon: 'what'};
brewBeer(myBeer as CraftBeer);
as
를 사용한 캐스팅과is
등의 키워드에 대해 별도로 공부할 것.
그럼에도 불구하고, 인터페이스 차원에서 정의되지 않은 속성들을 추가로 허용할 때는 아래와 같이 표현한다.
interface CraftBeer {
brand?: string;
[propName: string]: any;
여기서 이 대괄호 [] 가 의미하는 것은 무엇인지?
인터페이스는 함수의 타입을 정의할 때도 쓸 수 있다.
interface login {
(username: string, password: string): boolean;
}
문자열 username
, 문자열 password
라는 매개변수를 토대로 boolean
값을 반환하는 함수의 인터페이스
let loginUser: login;
loginUser = function(id: string, pw:string) {
console.log('로그인 했습니다.');
return true;
}
그냥 Arrow function이나
function 이름
형태로 선언할 수 없는 것인지?
함수와 클래스/객체 등에 범용 가능하게 작성할 수는 없는 것인지?
-> 뒤에서 나온다.
interface CraftBeer {
beerName: string;
nameBeer(beer: string): void;
}
class myBeer implements CraftBeer {
beerName: string = 'Baby Guinnes';
nameBeer(b: string) {
this.beerName = b;
}
constructor() {}
}
클래스와 마찬가지로 인터페이스도 확장이 가능
확장과 상속은 어떻게 다른 것인가?
interface Person {
name: string;
}
interface Developer extends Person {
skill: string;
}
let fe = {} as Developer;
fe.name = 'josh';
fe.skill = 'typescript';
혹은 아래와 같이 여러 인터페이스를 상속받아 사용할 수 있슴
interface Person {
name: string;
}
interface Drinker {
drink: string;
}
interface Developer extends Person {
skill: string;
}
let fe = {} as Developer;
fe.name = 'josh';
fe.skill = 'TypeScript';
fe.drink = 'Beer';
Drinker 는 여기서
extends
되지 않는데..?
함수 타입이면서, 객체 타입을 정의할 수 있는 인터페이스
interface CraftBeer {
(beer: string): string;
brand: string;
brew(): void;
}
function myBeer(): CraftBeer {
let my = (function(beer: string) {}) as CraftBeer;
my.brand = 'Beer Kitchen';
my.brew = function() {};
return my;
}
let brewedBeer = myBeer();
brewedBeer('My First Beer');
brewedBeer.brand = 'Pangyo Craft';
brewedBeer.brew();
그냥 쓰지 않는 게 좋을 것 같다....
https://www.typescriptlang.org/docs/handbook/interfaces.html