타입스크립트: 타입, 함수, 인터페이스

공대적 문과생·2022년 1월 19일
0

[TOC]

https://joshua1988.github.io/ts/intro.html

위 링크의 핸드북을 공부하면서, 새로 알게된 것들을 기록해본다. 틀리게 이해한 내용을 보시면 호되게 꾸짖어주세요.

기본 타입

타입명은 소문자로 쓴다

튜플

길이와 타입이 정해져 있는 배열.

let arr: [string, number] = ['hi',10];

이넘(enum)

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)한다.

never

함수의 끝에 절대 도달하지 않는다는 타입. 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는 클래스에서만 쓰는 거 아닌가? 함수에서 왜 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 하기 때문에, myBeerhop속성을 가지지 않아도 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

profile
공대적 문과생, 추남적 미남, 여성적 남성, 신사적 변태, 이론적 로맨티시스트, 현실적 이상주의자.

0개의 댓글