728x90
반응형
※ 인프런 - 타입스크립트 시작하기(by 이재승) 강의를 기반으로 정리한 내용입니다.
타입스크립트 기본 타입
export {};
const size: number = 123;
const isBig: boolean = size >= 100;
const msg: string = isBig ? "크다" : "작다";
// 배열의 타입 선언 방법은 아래와 같이 두 가지 방법이 있음
const values: number[] = [1, 2, 3];
const values2: Array<number> = [1, 2, 3];
// values.push("a"); // <--- error TS2345: Argument of type 'string' is not assignable to parameter of type 'number'.
// tuple타입: 각 인덱스 별로 타입을 미리 정의
const data: [string, number] = [msg, size];
data[0].substring(1);
// data[1].substring(1); // <--- error TS2339: Property 'substring' does not exist on type 'number'.
console.log("typeof 123 =>", typeof 123);
console.log("typeof 'abc' =>", typeof 'abc');
console.log("typeof [1,2,3] =>", typeof [1,2,3]);
export {};
// undefined와 null 역시 타입으로 정의 가능
let v1: undefined = undefined;
let v2: null = null;
// v1 = 123; // <--- error TS2322: Type '123' is not assignable to type 'undefined'.
// undefined와 null은 보통 다른 타입과 같이 사용됨
// 기호 " | " 은 유니온 타입
let v3: number | undefined = undefined;
v3 = 123;
console.log("typeof undefined =>", typeof undefined); // typeof undefinde => undefined
console.log("typeof null =>", typeof null); // typeof null => object
/**
* js에서 undefined는 undefined라는 타입으로 존재하지만,
* null은 타입으로 존재하지 않고 object로 표현됨
*/
// TS는 숫자와 문자열의 리터럴도 타입으로 정의 가능
let v4: 10 | 20 | 30; // v1은 10 or 20 or 30을 가질 수 있는 타입
v4 = 10;
// v4 = 15; // <--- error TS2322: Type '15' is not assignable to type '10 | 20 | 30'.
let v5: "경찰관" | "소방관";
// v5 = "의사"; // <--- error TS2322: Type '"의사"' is not assignable to type '"경찰관" | "소방관"'.
// any타입을 통해 모든 값을 포함하는 타입 사용 가능
let value: any;
value = 123;
value = "456";
value = () => {};
/**
* any타입은 기존에 자바스크립트 코드로 작성된 프로젝트를 타입스크립트로 포팅하는 경우 유용하게 사용 가능
* 기존 프로젝트의 모든 코드에 타입을 한 번에 정의하는 것은 부담되기 때문에 타입 에러가 나는 부분은
* 임시로 any타입으로 정의하면 됨
* any타입은 타입을 알 수 없는 경우나 타입 정의가 안 된 외부 패키지를 사용하는 경우에도 사용하기 좋음
* 단, any타입을 남발하면 TS 사용 의미가 퇴색되기 때문에 되도록 피하는 것이 좋음
*/
// void, never 타입
function f1(): void {
console.log("hello");
}
function f2(): never {
throw new Error("some error");
}
function f3(): never {
while (true) {
// ..
}
}
/**
* void: 아무 값도 반환하지 않고 종료되는 함수의 반환 타입은 void타입으로 정의할 수 있음
* never: 항상 예외가 발생해서 비정상적으로 종료되거나 무한루프 때문에 종료되지 않는 함수의 반환 타입은 never타입으로 정의
* 보통 never는 거의 사용하지 않음
*/
// 객체 타입 object
let v6: object;
v6 = {name: "dapsu"};
// console.log(v6.age); // <--- error TS2339: Property 'age' does not exist on type 'object'.
// 유니온( | ), 인터섹션( & )
let v7: (1 | 3 | 5) & (3 | 5 | 7);
v7 = 3;
// v7 = 1; // <--- error TS2322: Type '1' is not assignable to type '3 | 5'. 3과 5만 사용 가능하다는 문구 나옴
/**
* 유니온과 인터섹션을 이용하여 여러 타입의 교집합과 합집합을 표현할 수 있음
*/
// type키워드로 타입 별칭 주기
type width = number | string;
let width: width;
width = 100;
width = "100px";
enum타입
export {};
// enum 타입
enum Fruit {
Apple,
Banana = 5,
Orange,
}
const v1: Fruit = Fruit.Apple;
const v2: Fruit.Apple | Fruit.Banana = Fruit.Banana;
console.log(Fruit.Apple, Fruit.Banana, Fruit.Orange); // output: 0 5 6
// 이름과 값이 양방향으로 매핑됨
console.log(Fruit.Banana); // 5
console.log(Fruit["Banana"]); // 5
console.log(Fruit[5]); // Banana
/**
* enum타입은 js에는 없고 ts에만 있음
* 보통 자바와 같은 다른 언어에서는 enum이 존재함
* enum 안에 있는 원소는 타입뿐만 아니라 값으로도 사용 가능. 첫 번째 원소에 값을 할당하지 않으면 자동으로 0이 할당
* enum의 각 원소에는 숫자 또는 문자열을 할당할 수 있음
* 명시적으로 값을 입력하지 않으면 이전 원소에서 1만큼 증가한 값이 할당됨
* enum의 각 원소는 이름과 값이 양방향으로 매핑이 됨
*** 컴파일했을 때 ***
var Fruit;
(function (Fruit) {
Fruit[Fruit["Apple"] = 0] = "Apple";
Fruit[Fruit["Banana"] = 5] = "Banana";
Fruit[Fruit["Orange"] = 6] = "Orange";
})(Fruit || (Fruit = {}));
* enum은 객체로 사용되기 때문에 해당 객체를 런타임에 사용할 수도 있음
*/
enum Language {
Korean = "ko",
English = "en",
Japanese = "jp",
}
/**
* enum 아이템의 값은 숫자뿐만 아니라 문자열로도 입력 가능
* 문자열은 컴파일했을 때 숫자와 다른 코드가 나옴
*** 컴파일 ***
var Language;
(function (Language) {
Language["Korean"] = "ko";
Language["English"] = "en";
Language["Japanese"] = "jp";
})(Language || (Language = {}));
* enum의 원소에 숫자를 할당하면 양방향으로 매핑된 것과 다르게
* 문자열을 할당하는 경우에는 단방향으로 매핑됨
*/
function getIsValidEnumValue(enumObject: any, value: number | string) {
return Object.keys(enumObject) // enum 객체에서 모든 키를 뽑아냄
.filter(key => isNaN(Number(key))) // 양방향 매핑 때문에 filter를 이용하여 키가 숫자인 경우는 삭제
.some(key => enumObject[key] === value); // enum 객체 안에 입력된 value가 있는지 검사
}
/**
* enum을 제대로 이해했으면 위와 같은 유틸리티 함수를 작성할 수 있음
* 위 함수는 어떤 객체에 특정 value가 있는지 검사하는 함수
*
*/
// enum의 아이템의 이름은 false가 나오고 value는 true 나옴
console.log("1 in Fruit: ", getIsValidEnumValue(Fruit, 1)); // 1 in Fruit: false
console.log("5 in Fruit: ", getIsValidEnumValue(Fruit, 5)); // 5 in Fruit: true
console.log("Orange in Fruit: ", getIsValidEnumValue(Fruit, "Orange")); // Orange in Fruit: false
console.log("ko in Language: ", getIsValidEnumValue(Language, "ko")); // ko in Language: true
console.log("Korean in Language: ", getIsValidEnumValue(Language, "Korean")); // Korean in Language: false
// const enum 사용하기
const enum Books {
book1,
book2,
book3
}
const books: Books = Books.book1;
const enum Menu {
Menu1 = "samgyeopsal",
Menu2 = "hangjeongsal",
Menu3 = "gabrisal"
}
const menu: Menu = Menu.Menu1;
/**
* enum을 사용하면 컴파일 후에도 enum 객체가 남아있기 때문에 번들 파일의 크기가 불필요하게 커질 수 있음
* enum객체에 접근하지 않는다면 굳이 컴파일 후에 객체로 남겨 놓을 필요가 없음
* 이럴때는 const enum을 사용해서 컴파일 결과에 enum의 객체를 남겨 놓지 않을 수 있음
* 위의 코드 컴파일 결과:
* const books = 0 /* book1 *\/;
* const menu = "samgyeopsal" /* Menu1 *\/;
* enum객체는 없고 사용한 값만 노출됨!
* const enum을 사용하면 유틸리티 함수 사용할 수 없음. 이럴 때 Ts에서 const enum이라서 사용할 수 없다고 에러 줌
*/
함수타입1
export {};
function getText(name: string, age: number): string {
const nameText = name.substring(0, 10);
const ageText = age >= 35 ? "senior" : "junior";
return `name: ${nameText}, age: ${ageText}`;
}
// const v1: string = getText("mike", 23);
// const v2: string = getText("mike", "23"); // error TS2345: Argument of type 'string' is not assignable to parameter of type 'number'.
// const v3: number = getText("mike", 23); // error TS2322: Type 'string' is not assignable to type 'number'.
/**
* 위의 함수는 string타입이기 때문에 반환값이 string이어야 함
*/
// 화살표함수로는 이렇게 표현 가능
const getText2: (name: string, age: number) => string = (name, age) => { // 함수를 구현하는 코드에서는 타입을 정의할 필요 없음
return "";
}
// 선택 매개변수(optional parameters)
function getText3(name: string, age: number, language?: string): string {
const nameText = name.substring(0, 10);
const ageText = age >= 35 ? "senior" : "junior";
const languageText = language ? language.substring(0, 10) : "";
return `name: ${nameText}, age: ${ageText}, language: ${languageText}`;
}
// getText3("dapus", 30, "ko");
// getText3("dapus", 30);
// getText3("dapus", 30, 123); // error TS2345: Argument of type 'number' is not assignable to parameter of type 'string'.
/**
* 매개변수 이름 오른쪽에 물음표 기호를 입력하면 선택 매개변수가 됨
* language는 문자열도 가능하고 undefined일수도 있음
* 단, 문자열이 아닌 타입은 에러 발생
* 선택 매개변수는 마지막 순서에만 있어야 함
* 다만
unction getText3(name: string, age: number | undefined, language: string): string { ...
getText3("dapsu", undefined, "ko");
* 이렇게 사용이 가능하지만, 사용성과 가독성이 좋지 않음
*/
// 매개변수 디폴트값 설정
function getText4(name: string, age: number = 15, language = "ko"): string {
return "";
}
console.log(getText4('dapsu'));
/**
* age 매개변수 같은 경우, 변수에 값을 부여하지 않아도 15라는 디폴트값이 있음
* language는 string으로 타입을 정의하지 않았지만, 기본값이 "ko"라는 문자열이 있기 때문에 자동으로 string으로 정의
*
*/
// 나머지 매개변수(rest parameters)
function getText5(name: string, ...rest: number[]): string {
return "";
}
// console.log(getText5("dapsu", 1, 2, 3));
// console.log(getText5("dapsu", 1, 2, "3")); // <--- error TS2345: Argument of type 'string' is not assignable to parameter of type 'number'.
/**
* rest parameters의 타입은 항상 배열로 정의해야 함
* 위에서는 number의 배열로 정의되어 있기 때문에 문자열이 변수로 들어가면 에러남
*/
함수타입2
export {};
// 타입스크립트에서 this 정의하는 방법
function getParam(this: string, index: number): string {
const params = this.split(",");
if (index < 0 || params.length <= index) {
return "";
}
return this.split(",")[index];
}
/**
* this의 타입은 매개변수 맨 앞에 정의할 수 있음
* 타입스크립트는 this가 있으면 맨 앞의 매개변수를 this의 타입이라고 인식함
* 그래서 함수의 매개변수는 두 번째부터 시작함
*/
// 인터페이스를 이용하여 String에 getParam 메소드 추가
// interface String {
// getParam(this: string, index: number): string;
// }
// String.prototype.getParam = getParam;
// console.log("asdf, 1234, ok".getParam(1)); // 1234
/**
* 자바스크립트에 내장된 타입에 기능을 주입하고 싶을 때는 프로토타입을 이용해서 주입할 수 있음
* 그러나 내장 타입에 getParam이라는 속성이 없기 때문에 interface를 이용하여 속성 추가 가능
*/
// // Object에 메소드 추가하기
// interface Object {
// getShortKeys(this: object): string[];
// }
// Object.prototype.getShortKeys = function () {
// return Object.keys(this).filter(key => key.length <= 3);
// };
// const obj = {
// a: 1,
// bb: 2,
// ccc: 3,
// dddd: 4
// };
// console.log(obj.getShortKeys()); // [ 'a', 'bb', 'ccc' ]
// add함수
/** 조건
* 두 매개변수가 모두 문자열이면 문자열 반환
* 두 매개변수가 모두 숫자이면 숫자 반환
* 두 매개변수를 서로 다른 타입으로 입력하면 안 된다.
*/
function add(x: number, y: number): number;
function add(x: string, y: string): string;
function add(x: number | string, y: number | string): number | string {
if (typeof x === 'number' && typeof y === 'number') return x + y;
else {
const result = Number(x) + Number(y);
return result.toString();
}
}
// const v1:number = add(1, 2); // 매개변수 둘 다 숫자지만, v1이 number타입만 정의되어서 문제
// console.log(add(1, '2')); // 매개변수가 서로 다른 타입이지만 에러가 발생하지 않음
/**
* add함수 위에 코드 두 줄 넣으면 됨!
function add(x: number, y: number): number;
function add(x: string, y: string): string;
*/
// named parameters 방식
// 매개변수 타입 정의를 객체로 정의
function getText({
name,
age = 15,
language
}: {
name: string;
age?: number;
language?: string;
}): string {
const nameText = name.substring(0, 10);
const ageText = age >= 35 ? 'senior' : 'junior';
return `name: ${nameText}, age: ${ageText}, language: ${language}`;
}
getText({name: 'nike'})
getText({name: 'dapsu', age: 30, language: 'ko'})
// 인터페이스로 타입 정의를 따로 만들수도 있음
interface Param {
name: string;
age?: number;
language?: string;
}
function getText2({name, age = 15, language}: Param): string {
const nameText = name.substring(0, 10);
const ageText = age >= 35 ? 'senior' : 'junior';
return `name: ${nameText}, age: ${ageText}, language: ${language}`;
}
/**
* 매개변수가 많아지면 가독성이 떨어지기 때문에 named parameters로 변경하는 것이 좋음
* 하지만 위의 방법을 수동으로 하면 다소 번거로움
* 타입스크립트에서 자체적으로 변환해주는 기능이 있음
*/
// function에 커서를 대면 좌측 상단에 전구 모양 표시됨. 클릭하면 Convert paramteters to destructed object 뜸!
function getText3(name: string, age: number = 15, language?: string): string {
const nameText = name.substring(0, 10);
const ageText = age >= 35 ? 'senior' : 'junior';
return `name: ${nameText}, age: ${ageText}, language: ${language}`;
}
인터페이스
/**
* 자바와 같은 다른 언어에서 인터페이스는 클래스를 구현하기 전에 필요한 메소드를 정의하는 용도로 쓰임
* 타입스크립트에서는 더 다양한 것들을 정의하는데 사용됨
* 타입스크립트에서 인터페이스로 정의할 수 있는 타입의 종류와 인터페이스로 타입을 정의하는 방법에 대해서 알아보자.
*/
export {};
// 객체에 타입을 정의하는 방법
/**
* 인터페이스 키워드 오른쪽에 타입의 이름을 적는다.
* 괄호 안에 필요한 속성을 입력한다.
* 선택속성은 속성 이름 오른쪽에 ? 기호를 붙인다.
* readonly속성은 읽기 전용이기 때문에 변경 불가능
*/
interface Person {
name: string;
readonly nickname: string;
age?: number;
// age: number | undefined; // 이 방식은 선택속성 아님. age 항상 입력해야 함
}
const p1: Person = {name: 'leo', nickname: 'dapsu', age: 30};
// p1.nickname = 'handsom guy'; // 읽기 전용 속성으로 에러 발생
// 객체가 정의되지 않은 속성값을 가지고 있어도 할당 가능
const p2 = {
name: 'mike',
nickname: 'nike',
birthday: '2022-02-22'
}
const p3: Person = p2; // 가능한 이유? p3타입이 p2타입을 포함하는 더 큰 타입이기 때문(타입 호환성 부분에서 자세히 다룰 예정)
// 인덱스 타입: 인터페이스에서 속성 이름을 구체적으로 정의하지 않고 값의 타입만 정의하는 것
interface Person2 {
name: string;
age: number;
[key: string]: string | number; // 인덱스 타입
}
const p4: Person2 = {
name: 'loo',
birthday: "2022-02-22", // 인덱스 타입에 의해 가능
age: 25,
};
// 자바스크립트는 속성 이름이 숫자면 내부적으로 문자열로 변환해서 사용함
// 타입스크립트에서는 숫자인 속성 이름의 값이 문자열인 속성 이름의 값으로 할당 가능한지 검사함
interface YearPriceMap {
[year: number]: number; // 이 숫자의 값 A는 B로 할당이 가능해야 한다.
[year: string]: string | number; // 이게 가능하기 위해서는 문자열과 숫자 둘다 정의되어야 함
}
const yearMap: YearPriceMap = {};
// yearMap[1998] = 1000;
// yearMap[1998] = '1000'; // 에러
// yearMap['2000'] = 1234;
// yearMap['2000'] = '1234'; // 이것도 에러. 왜? 타입스크립트 4.4부터 스펙 변경. 인덱스로 문자열을 입력해도 숫자로 파싱 가능하면 숫자로 인식
// 인터페이스로 함수 타입 정의
interface getText {
(name: string, age: number): string;
}
// 위의 인터페이스와 같은 것임: type GetText = (name: string, age: number) => string;
const getText: getText = function(name, age) {
const nameText = name.substring(0, 10);
const ageText = age >= 35 ? 'senior' : 'junior';
return `name: ${nameText}, age: ${ageText}`;
}
// 자바스크립트에서는 함수도 속성값을 가질 수 있음
interface getText2 {
(name: string, age: number): string;
totalCall?: number; // 인터페이스를 정의할 때 함수의 속성값도 이렇게 정의할 수 있음
}
const getText2: getText2 = function(name, age) {
if (getText2.totalCall !== undefined) {
getText2.totalCall++;
console.log(`totalCall: ${getText2.totalCall}`);
}
return "";
}
// getText2.totalCall = 0;
// getText2('', 0);
// getText2('', 0);
// 인터페이스 class로 구현
interface Person3 {
name: string;
age: number;
isYoungerThan(age: number): boolean;
}
class SomePerson implements Person3 {
name: string;
age: number;
constructor(name:string, age: number) {
this.name = name;
this.age = age;
}
isYoungerThan(age: number) {
return this.age < age;
}
}
// 인터페이스 확장
interface Person4 {
name: string;
age: number;
}
interface Korean extends Person4 {
isLiveInSeoul: boolean;
}
/**
* Korean에는 Person4의 속성들과 isLiveSeoul의 속성이 들어있음
interface Korean extends Person4 {
name: string;
age: number;
isLiveInSeoul: boolean;
}
*/
// 인터페이스 여러 개로 확장도 가능
interface Programmer {
language: string;
}
interface Korean2 extends Person4, Programmer {
city: string;
}
// &으로 속성값의 교집합
interface Product {
name: string;
price: number;
}
type PP = Person4 & Product;
const pp: PP = {
name: 'a',
age: 23,
price: 1000
};
// 교집합이 속성에 대한 교집합이 아니라, 타입이 가질 수 있는 값의 집합에 대한 교집합이기 때문에 가능(자세한 것은 타입 호환성 부분에서!)
클래스
export {};
class Person {
name: string;
// private name: string;
// #name: string; // 속성 이름 앞에 # 붙이면 private으로 정의한다는 것과 동일함
constructor(name: string) {
this.name = name;
// this.#name = name;
}
sayHello() {
console.log(`Hola!! Me yamo ${this.name}`);
// console.log(`Hola!! Me yamo ${this.#name}`);
}
}
class Programmer extends Person {
constructor(name: string) {
super(name); // 자식 클래스의 컨스트럭터에서는 반드시 super를 호출해야 함(부모 클래스의 컨스트럭터가 호출됨 )
}
// method override: 부모의 sayHello가 아닌 자식의 함수가 호출됨
sayHello() {
// this.name; // 만약 부모 클래스의 name이 private이면 에러 발생
// this.#name; // 만약 부모 클래스의 name이 private이면 에러 발생
super.sayHello(); // 부모의 함수를 호출하기 위해서는 super.sayHello()를 호출해야 함
console.log('난 프로그래머다');
}
}
const programmer = new Programmer('dapsu');
programmer.sayHello();
// Hola!! Me yamo dapus
// 난 프로그래머다
const person = new Person('홍길동');
console.log(person.name); // private일 때 에러남
// 접근범위 설정 키워드: public, protected, private
/**
* public: 외부에도 노출하면서 상속받는 쪽에도 노출을 하는 것
* private: 외부에도 노출하지 않고 상속받는 쪽에도 노출하지 않는 것
* protected: 외부에는 노출하지 않지만 상속받는 쪽에만 노출
* 메소드를 정의할 때 따로 설정하지 않으면 기본값으로 public이 사용됨
*/
// 타입스크립트 접근범위 편의 기능
class Person2 {
// 변수 선언, 초기화하는 코드도 필요 없음. 자동으로 해줌
constructor(public name: string, public age: number) {}
}
const person2 = new Person2('dapsu', 30);
console.log(person2.name, person2.age); // dapsu 30
// getter, setter
class Person3 {
private _name: string = '';
// getter 정의
get name(): string {
console.log('getter called');
return this._name;
}
// setter 정의
set name(newName: string) {
if (newName.length > 10) {
throw new Error('최대 길이를 넘었습니다');
}
this._name = newName;
}
}
let person3 = new Person3();
person3.name = "dapsu"; // getter called
console.log(person3.name); // dapsu
// person3.name = "우주제일초절정킹갓미소년"; // Error: 최대 길이를 넘었습니다
// static키워드 이용하여 정적 멤버 변수와 정적 메소드 정의
class Person4 {
static adultAge = 20;
constructor(public name: string, public age: number) {}
sayHello() {
console.log(`안녕하세요 저는 ${this.name}입니다`);
console.log(
Person4.getIsAdult(this.age) ? '삐빅! 성인입니다' : "삐빅! 청소년입니다"
);
}
static getIsAdult(age: number) {
return Person4.adultAge <= age; // adultAge 정적변수이기 때문에 접근 가능
}
}
/**
* static 키워드가 붙은 속성은 객체와 상관없이 고정값
* 그래서 사용할 때 클래스이름에 . 찍어서 접근할 수 있음
*/
const person4 = new Person4('killdong', 24);
person4.sayHello();
// 안녕하세요 저는 killdong입니다
// 삐빅! 성인입니다
console.log(`성인 판단 기준 나이: ${Person4.adultAge}`); // 성인 판단 기준 나이: 20
// abstract키워드로 추상클래스 정의
abstract class Person5 {
constructor(public name: string) {}
sayHello() {
console.log(`Hey, I'm ${this.name}`);
}
abstract sayHello2(): void;
}
class Student extends Person5 {
sayHello() {
super.sayHello();
console.log("I'm a student");
}
// 이 함수 상속받지 않으면 에러뜸
sayHello2(): void {
console.log("Wassssssssssssup!!!!!!!!!");
}
}
// const person5 = new Person5('Tom'); // 추상클래스는 객체로 만들 수 없음
728x90
반응형
'개발자 도전기 > [STUDY] JS || TS' 카테고리의 다른 글
TypeScript | 강의 메모 | 제네릭(Generic), 맵드 타입(Mapped Type), 조건부 타입(Conditional Type) (0) | 2022.04.01 |
---|---|
TypeScript | 강의 메모 | 타입 호환성 (0) | 2022.04.01 |
TypeScript | 강의 메모 | 타입스크립트란? (0) | 2022.03.30 |
JavaScript | FP && ES6+ | L.flatten, L.flatMap, flatMap, 지연성/이터러블 실무 예제 (0) | 2022.02.16 |
JavaScript | FP && ES6+ | 지연성 (0) | 2022.02.16 |
댓글