https://joshua1988.github.io/ts/guide/functions.html#this
1. 기본타입
기본 타입은 크게 12가지가 있다.
Boolean - 타입이 진위값인 경우
let isLoggedIn : boolean = false;
Number - 타입이 숫자일 때
let num : number = 10;
String - 타입이 문자일 때
let str : string = 'hi';
Object
Array - 타입이 배열인 경우는 아래처럼 선언
let arr: Array<number> = [1, 2, 3]
Tuple - 배열의 길이가 고정되고, 각 요소의 타입 지정되어있는 배열 형식
let arr: [string, number] = ['hi', 10];
Enum - 자바, C에서 흔히 쓰이는 타입으로 특정값(상수)들의 집합을 의미
enum Avengers {Capt, IronMan, Thor}
let hero : Avengers = Avengers.Capt;
Any - 모든 타입에 대해 허용
Void - 변수에 undefined와 null만 할당하고, 함수에는 반환값을 설정할 수 없는 타입
let unuseful : void = undefined;
function notuse() : void{
console.log('sth');
}
Null
Undefined
Never - 함수의 끝에 절대 도달하지 않는다는 의미를 지닌 타입
// 이 함수는 절대 함수의 끝까지 실행되지 않는다는 의미
function neverEnd() : never {
while (true) {
}
}
2. 함수
웹 애플리케이션 구현 시 자주 사용되는 함수는 타입스크립트로 크게 다음 3가지 타입 정의할 수 있다.
1. 함수의 파라미터(매개변수) 타입
2. 함수의 반환 타입
3. 함수의 구조 타입
# 함수의 기본적인 타입 선언:
매개변수와 합수의 반환값에 타입을 추가 (반환값 타입 정하지 않을 때는 void라도 사용)
function sum(a: number, b: number) : number {
retutn a + b;
}
# 함수의 인자:
타입스크립트에서는 함수의 인자를 모두 필수값으로 간주한다.
따라서, 함수의 매개변수 설정 시, undefined나 null 이라도 인자로 넘겨야 하며,
컴파일러에서 정의된 매개변수 값이 넘어왔는지 확인한다. 정의된 매개변수 값 외에 추가로 값을 받을 순 없다.
하지만 자바스크립트에서는 매개변수 갯수만큼 인자를 넘기지 않아도 된다.
이 특성 살리고 싶으면 ? 을 이용하다.
매개변수 초기화 시에는 타입을 붙이지 않는다.
function sum(a: number, b='100'): number {
return a + b;
}
# ES6 문법에서 지원하는 REST 문법
function sum(a: number, ...nums: number[]) : number {
const totalOfNums = 0;
for (let key in nums) {
totalOfNums += nums[key];
}
return a + totalOfNums;
}
cf) javascript rest 문법(https://learnjs.vlpt.us/useful/07-spread-and-rest.html)
const purpleCuteSlime = {
name: '슬라임',
attribute: 'cute',
color: 'purple'
};
const { color, ...rest } = purpleCuteSlime;
console.log(color);
console.log(rest);
//꼭 rest라 쓰지 않아도 됨.
const purpleCuteSlime = {
name: '슬라임',
attribute: 'cute',
color: 'purple'
};
const { color, ...cuteSlime } = purpleCuteSlime;
console.log(color);
console.log(cuteSlime);
const { attribute, ...slime } = cuteSlime;
console.log(attribute);
console.log(slime);
// 함수에서 rest를 사용할 때
// 다음의 경우 파라미터가 7개라 인자 7개가 가야 하는데 6개만 오기 때문에 마지막 하나인 g가 undefined가 돼서
// 합했을 때 숫자 + undefined = NaN이 되어버린다.
function sum(a, b, c, d, e, f, g) {
let sum = 0;
if (a) sum += a;
if (b) sum += b;
if (c) sum += c;
if (d) sum += d;
if (e) sum += e;
if (f) sum += f;
if (g) sum += g;
return sum;
}
const result = sum(1, 2, 3, 4, 5, 6);
console.log(result);
// 이를 방지하기 위해 아래와 같이 rest를 사용할 수 있다.
function sum(...rest) {
return rest.reduce((acc, current) => acc + current, 0);
}
const result = sum(1, 2, 3, 4, 5, 6);
console.log(result); // 21
this가 가리키는 것 명시하려면 아래와 같은 문법 써준다.
interface Vue {
el: string;
count: number;
init(this: Vue) : () => {};
}
let vm: Vue = {
el: '#app',
count: 10,
init: function(this: Vue) {
return () => {
return this.count;
}
}
}
let getCount = vm.init();
let count = getCount();
console.log(count);
만약 콜백으로 함수가 전달되었을 때 this를 구분해줘야 하는데 이럴 땐 아래와 같이 강제할 수 있다.
interface UIElement {
//아래의 함수에서 'this:void' 코드는 함수에 'this' 타입을 선언할 필요가 없다는 의미입니다.
addClickListener(onclick: (this: void, e: Event) => void) : void;
}
// class Handler {
// info: string;
// onClick(this: Handler, e: Event) {
// // 위의 `UIElement` 인터페이스의 스펙에 this는 필요없다 했지만 사용했기 때문에 에러가 발생합니다.
// this.info = e.message;
// }
// }
class Handler {
info: string;
onClick(this: void, e: Event) {
console.log('clicked')
}
}
let handler = new Handler();
uiElement.addClickListener(handler.onClick);
이게 바로 클래스의 메서드 방식으로 선언하는 것과 변수에 화살표 함수를 연결하는 것의 차이점이다.
3. 인터페이스
인터페이스는 상호 간에 정의한 약속, 혹은 규칙을 의미한다.
타입스크립트에서 인터페이스는 보통 다음의 범주에 대해 약속을 정의한다.
- 객체의 스펙(속성과 속성의 타입)
- 함수의 파라미터
- 함수의 스펙(파라미터, 반환 타입 등)
- 배열과 객체를 접근하는 방식
- 클래스
# 인터체이스에 대해 알아볼 간단한 예제를 보자
let person = {naem: 'Capt', age: 28};
function logAge(obj: {age: number}) { // 인자를 받을 때 객체의 속성타입까지 정의 가능
console.log(obj.age);
}
logAge(person); //28
만약 여기에 인터페이스를 적용한다면?
interface personAge {
age: number;
}
function logAge(obj: personAge) { //인자가 'personAge'란 이름 얻어 보다 명시적으로 바뀜
console.log(obj.age);
}
let person = {name: 'Capt', age: 28};
logAge(person);
인터페이스를 인자로 받아 사용할 때는 '인터페이스에 정의된 속성 개수'가 '인자의 속성 개수' 보다 적어도 된다.
하지만 인자의 속성은 인터페이스에 정의된 속성 개수는 반드시 포함해야 한다.(옵션 속성 제외)
또 속성 순서를 지키지 않아도 된다.
# 옵션 속성: 속성에 ? 붙이기.
interface 인터페이스_이름 {
속성? : 타입;
}
# 옵션 속성의 장점
단순히 인터페이스 사용할 때 속성 선택적으로 적용가능한 것 뿐 아님!
인터페이스에 정의되지 않은 속성을 인지시키는 것이 가능하다는 것이다.
# 읽기 전용 속성
읽기 전용 속성은 인터페이스로 객체 처음 생성할 때만 값 할당하고, 그 이후에는 변경 불가능한 속성을 의미한다.
문법은 다음과 같이 readOnly 속성을 앞에 붙인다.
interface CraftBeer {
readonly brand: string;
}
# 읽기 전용 배열
배열 선언 시 ReadonlyArray<T> 타입 사용하면 읽기 전용 배열 생성 가능하다.
let arr: ReadonlyArray<number> = [1, 2, 3];
arr.splice(0,1); //errror
arr.push(4); //errror
arr[0] = 100; //errror
이처럼 배열을 ReadonlyArray로 선언 시 배열의 내용 변경할 수 없다. 선언 시점에만 값 정의 가능하다.
# 객체 선언과 관련된 타입 체킹
타입스크립트는 인터페이스를 이용해 객체를 선언할 때 좀 더 엄밀한 속성 검사를 진행한다.
만약 까다로운 타입 추론을 무시하고 싶다면 아래와 같이 선언한다.
let myBeer = { brandon: 'what' };
brewBeer(myBeer as CraftBeer);
그럼에도 불구하고 인터페이스에서 정의하지 않은 속성들을 추가로 사용하고 싶다면?
이렇게 하면 된다.
interface CraftBeer {
brand?: string;
[propName: string]: any;
}
# 함수 타입
인터페이스는 함수의 타입을 정의할 때도 쓸 수 있다.
interface login {
(username: string, password: string): boolean;
}
let loginUser : login;
loginUser = function(id: string, pw: string) {
console.log('로그인 했습니다.');
return true;
}
# 클래스 타입
C#이나 자바처럼 타입스크립트에서도 클래스가 일정 조건을 만족하도록 타입 규칙을 정할 수 있다.
interface CraftBeer {
beerName: string;
nameBeer(beer: string) : void;
}
class myBeer implements CraftBeer {
beerName: string = 'Baby Guinness';
nameBeer(b: string) {
this.beerName = b;
}
constrouctor () {}
}
const newBeer = new myBeer();
console.log(newBeer.beerName);
console.log(newBeer.nameBeer('Good Shot'));
console.log(newBeer.beerName);
# 인터페이스 확장
클래스와 마찬가지로 인터페이스도 인터페이스 간 확장이 가능하다.
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, Drinker {
skill: string;
}
let fe = {} as Developer;
fe.name = 'josh';
fe.skill = 'TypeScript';
fe.drink ='Beer';
# 하이브리드 타입
인터페이스는 다음과 같이 함수 타입이면서 객체 타입을 정의할 수 있는 인터페이스처럼,
여러가지 타입을 조합해 만들 수 있다.
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();
# 클래스를 상속받는 인터페이스
interface가 클래스의 상속을 받을 때 내부의 private, protected 속성들도 모두 상속받는다.
이렇게 클래스를 상속받는 interface를 implement하려면 클래스나 하위 클래스 역시 extends(상속) 받아야 한다.
class Control {
private state: any;
}
interface SelectableControl extends Control {
select(): void;
}
class Button extends Control implements SelectableControl {
select() {}
}
class TextBox extends Control {
select() {}
}
class ImageControl implements SelectableControl {
// Class 'ImageControl' incorrectly implements interface 'SelectableControl'.
// Types have separate declarations of a private property 'state'.
private state: any;
select() {}
}
4. 이넘
이넘은 특정 값들의 집합을 의미하는 자료형.
1. 숫자형 이넘 선언
enum Direction {
Up = 1, //1
Down, //2
Left, //3
Right //4
}
숫자형 이넘에 초기값을 주면 초기값부터 차례로 1씩 증가
초기값 주지 않으면 0부터 차례로 1씩 증가
enum Direction {
Up, // 0
Down, // 1
Left, // 2
right // 3
}
숫자형 이넘은 다음처럼 사용 가능하다.
enum Response {
No = 0,
Yes = 1,
}
function respond(recipient: string, message: Response): void {
// ...
}
respond("Captain Pangyo", Response.Yes);
문자형 이넘은 반드시 초기값을 줘야 한다
enum Direction {
Up = "UP",
Down = "DOWN",
Left = "LEFT",
Right = "RIGHT",
}
복합 이넘(권고X, 최대한 같은 타입으로 이루어진 이넘 사용 추천)
enum BooleanLikeHeterogeneousEnum {
No = 0,
Yes = "YES",
}
런타임 시점에서의 이넘 => 런타임 시 이넘은 실제 객체 형태로 존재한다.
가령 아래와 같은 이넘 코드 있을 때
enum E {
X, Y, Z
}
function getX(obj: { X: number }) {
return obj.X;
}
getX(E); // 이넘 E의 X는 숫자이기 때문에 정상 동작
컴파일 시점에서의 이넘 => 런타임에서는 실제객체이지만 keyof 대신 keyof typeof를 사용해야 한다.
enum LogLevel {
ERROR, WARN, INFO, DEBUG
}
//'ERROR' | 'WARN' | 'INFO' | 'DEBUG'
type LogLevelStrings = keyof typeof LogLevel;
function printImportant(key:LogLevelStrings, message: string) {
const num = LogLevel[key];
if (num <= LogLevel.WARN) {
console.log('Log Level key is',key);
console.log('Log Level value is',num);
console.log('Log Level message is',message)
}
}
printImportant('ERROR', 'This is Message')
리버스 매핑 => 숫자형 이넘에만 존재하는 특징. 이넘의 키(key) 로 값을 얻을 수 있고 값으로 키를 얻을 수 있다.
enum Enum {
A
}
let a = Enum.A; // 키로 값을 획득
let keyName = Enum[a]; // 값으로 키를 획득
'개발 공부 > 타입스크립트' 카테고리의 다른 글
타입스크립트 가이드 읽기 - 1. The Basics (0) | 2023.02.02 |
---|