npm i typescript -D // 개발모드에 TS설치
npx tsc --init // 타입스크립트의 컴파일 옵션을 기본으로 실행함
npx tsc // 프로젝트 폴더에 안에 있는 모든 TS파일이 tsconfig에 맞춰서 JS로 컴파일됨
let union: string | null = null;
//create by object literal
const person1 = {name: 'gu', age: 24};
//person1은 "object" type이 아님, person1은 "{name: string, age: number}" type
//create by Object.create
const person2 = Object.create({name: 'gu', age: 24});
let list: Array<number> = [1, 2, 3];
let list: number[] = [1, 2, 3];
let x: [string, number];
x = ["hello", 24];
x = [10, "hello"]; //error
x[2] = 'go'; //error
let looselyTyped: any = {};
let d = looselyTyped.a.b.c.d;
//위의 코드는 코드 작성 시 오류가 발생하지 않으며 d의 타입은 any로 추론됨.
declare const maybe: unknown;
const aNumber: number = maybe; //error
if (maybe === true) {
const aBoolean: boolean = maybe;
const aString: string = maybe; //error
}
if(typeof maybe === 'string'){
const aString:string = maybe;
const aBoolean: boolean = maybe; //error
}
unknown 타입은 위와 같이 타입가드를 통해서 타입을 한정시켜야지만 사용할 수 있음
function error(message: string): never{
throw new Error(message);
}
function fail(){
return error('failed');
}
function infiniteLoop(): never{
while(true) {
}
}
함수의 반환 값이 없다고 명시적으로 작성하는 타입
function returnVoid(message: string): void {
console.log(message);
return;
}
returnVoid("no return");
enum Animals { Cat, Dog, Lion }
console.log(Animals[0]); // Cat
console.log(Animals.Cat); // 0
enum Animals { Cat = 1, Dog, Lion }
console.log(Animals[1]); // Cat
console.log(Animals[3]); // Lion
enum Animals {
Cat = 'meow',
Dog = 1,
Lion = 2,
}
console.log(Animals.Cat); // meow
console.log(Animals[0]); //undefined
let uni: string | number = 0;
uni = 'hi';
uni = 123;
type UserA = {
name: string;
age: number;
};
type UserB = {
isValid: boolean;
};
const userA: UserA = {
name: 'a',
age: 12,
isValid: true, // error
};
const userB: UserB = {
name: 'B', // error
age: 11,
isValid: true,
};
const userC: UserA & UserB = {
name: 'C', age: 40, isValid: false
};
let a = 'Hello'; // string이라는 별도의 타입을 명시할 필요가 없음
a = 123; // error
function join(a: string, b = '') { //b 매개변수에 타입을 명시할 필요가 없음
return a + b; // 함수의 반환 타입도 작성하지 않아도 됨
}
const a: number = join('hello', 'world'); // error
// as 키워드를 통해 btn이 버튼 엘리멘트임을 단언함
const btn = document.querySelector('button') as HTMLButtonElement;
// (btn as HTMLButtonElement).classList.add('btn'); 나중에 단언도 가능
// ! 키워드를 통해 btn 변수가 null이나 undefined가 아님을 단언함
const btn = document.querySelector('button')!;
// btn!.classList.add('btn'); 나중에 단언도 가능
// as 키워드 사용 예
function toTwoDecimals(val: number | string, isNum: boolean) {
if (isNum) {
(val as number).toFixed(2);
} else {
(val as string).slice(0, 2);
}
}
toTwoDecimals(3.141592, true);
toTwoDecimals('hi', false);
const json = '{"name": "gugu", age: 22}';
const user = JSON.parse(json);
console.log(user.email); // 에러가 발생하지 않음
// as 키워드 사용 예
const json = '{"name": "gugu", age: 22}';
const user = JSON.parse(json) as { name: string, age: number};
console.log(user.email); // error
let num: number;
console.log(num); // 초기화가 되지 않아 undefined이므로 오류 발생
let num!: number; // 할당 단언
console.log(num); // 초기화가 되어졌다고 typescript가 판단하기 때문에 오류가 발생하지 않음
const btn = document.querySelector('button');
if (btn) { // if 조건을 통해 btn이 null이 아님이 보장됨
btn.classList.add('btn');
btn.id = 'abc';
}
if (btn instanceof HTMLButtonElement) { // 해당 방법도 가능
btn.classList.add('btn');
btn.id = 'abc';
}
function toTwoDecimals(val: number | string) {
if (typeof val === 'number') { // typeof를 통해 val의 타입 가드를 형성함
val.toFixed(2);
} else {
val.slice(0, 2);
}
}
toTwoDecimals(3.14);
toTwoDecimals('hi');
type UserA = { name: string; age: number };
type UserB = { id: string; email: string };
// is 키워드를 통해 isUserA라는 함수가 UserA 인것을 확인하는 용도의 함수라는 것을 알림
function isUserA(user: unknown): user is UserA {
if (user && user.constructor === Object) {
const u = user as UserA;
return typeof u.name === 'string' && typeof u.age === 'number';
}
return false;
}
fetch('https://test.com')
.then((res) => res.json())
.then((user: UserA | UserB) => {
if (isUserA(user)) {
console.log(user.name[0]);
console.log(user.age - 10);
}
});
type MyStringType = string;
const str = 'world';
let myStr: MyStringType = 'hello';
myStr = str;
type StringOrNumber = string | number;
let another: StringOrNumber = 0;
another = 'Hi';
type PersonTuple = [string, number];
let another: PersonTuple = ['gu', 24];
type EatType = (food: string) => void;
개체(객체, 배열, 함수, 클래스 등)를 정의하는 타입
interface 인터페이스_이름 {
속성?: 타입;
}
위의 코드와 같이 속성뒤에 ?를 붙이게 되면 해당 속성은 옵션으로 변하게 됨 ⇒ type에서도 사용이 가능함
인덱싱 가능한 타입을 만들 때 인터페이스의 인덱스 시그니처 방식을 이용함
interface Arr {
[key: number]: string;
}
const arr: Arr = ['A', 'B', 'C'];
console.log(arr[1]); // B
interface ArrLike {
[key: number]: string;
}
const arrLike: ArrLike = { 0: 'A', 1: 'B', 2: 'C' };
console.log(arrLike[1]); // B
interface Obj {
[key: string]: string;
}
const obj: Obj = { a: 'A', b: 'B', c: 'C' };
console.log(obj['b']); // B
console.log(obj.b); // B
interface Person3 {
name: string;
age: number;
[index: string]: string | number;
}
const p32: Person3 = {
name: 'gugu',
email: 'test@test.com',
person_id: 1,
age: 23,
};
인터페이스라는 하나의 타입 안에 들어있는 속성의 또 다른 타입을 타입 인덱싱을 통해 얻어낼 수 있음
interface User {
name: string;
age: number;
}
const a: User['name'] = 'gugu';
const b: User['age'] = 123;
interface Person4 {
name: string;
age: number;
hello(): void;
}
const p41: Person4 = {
name: 'gugu',
age: 12,
hello: function(): void {
console.log(`hi ${this.name}`);
}
};
const p42: Person4 = {
name: 'ququ',
age: 11,
hello(): void{
console.log(`hi ${this.name}`);
}
}
// arrow function에서는 this를 사용할 수 없기 때문에 오류 발생
// const p43: Person4 = {
// name: 'dudu',
// age: 11,
// hello: (): void => {
// console.log(`hi ${this.name}`);
// }
// }
p41.hello();
p42.hello();
인터페이스를 이용해서 클래스(생성(구문) 시그니처(Construct signature))를 만들어내는 방법
interface UserI {
name: string;
getName(): string;
}
interface UserC {
new (n: string): UserI; // 생성 시그니처
}
class User implements UserI {
public name;
constructor(name: string) {
this.name = name;
}
getName() {
return this.name;
}
}
const user = new User('Gugu');
user.getName(); // Gugu
// userClass에는 생성 시그니처 타입이 정의되어야 함
function hello(userClass: UserC, msg: string) {
const user = new userClass('Gugu');
return `hello ${user.getName()} ${msg}`;
}
hello(User, 'hi');
인터페이스를 확장하는데 사용함
interface IPerson2{
name: string;
age?: number;
}
interface IKorean extends IPerson2{
city: string;
}
const k: IKorean = {
name: 'gugu',
city: 'seoul',
age: 12,
};
interface User {
name: string;
age: number;
}
interface User { // 첫 부분의 User와 합쳐짐
isValid: boolean;
}
const user: User = {
name: 'Gu',
age: 24,
isValid: true
}
interface FullName {
first: string;
last: string;
}
interface FullName {
mid: string;
last: boolean // error
// 중복해서 인터페이스를 정의할 경우 기존에 존재하는 속성에 다른 타입을 지정할 수 없음
}
interface HelloPerson {
(name: string, age?: number): void;
}
const helloPerson: HelloPerson = function(name: string, age?: number) {
console.log(`hi ${name}`)
}
interface Person8 {
name: string;
age?: number;
readonly gender: string;
}
const p81: Person8 = {
name: 'gugu',
gender: 'male',
}
p81.gender = 'female'; //읽기 전용 속성이므로 'gender'에 할당할 수 없음
interface User {
name: string
}
// this의 타입을 명시적으로 정의할 수 있음
function greet(this: User, msg: string) {
return `${msg}, ${this.name}`;
}
const gu = {
name: 'gu',
greet
};
gu.greet('Hi'); // Hi, gu
const neo = {
name: 'Neo',
};
greet.call(neo, 'Bye');
type alias: 어떤 타입을 부르는 이름을 말함
interface: 새로운 타입을 만들어 내는 것 ⇒ 객체 타입을 정의할 땐 interface를 주로 사용함
//type alias
type EatType = (food: string) => void;
//interface
interface IEat {
(food: string): void;
}
//type alias
type PersonList = string[];
//interface
interface IPersonList {
[index: number]: string;
}
interface ErrorHandling {
success: boolean;
error?: { message: string };
}
interface ArtistsData {
artists: { name: string }[];
}
// type alias
type ArtistsResponseType = ArtistsData & ErrorHandling;
// interface
interface IArtistsResponse extends ArtistsData, ErrorHandling {}
let art: ArtistsResponseType;
let iar: IArtistsResponse;
interface Bird {
fly(): void;
layEggs(): void;
}
interface Fish {
swim(): void;
layEggs(): void;
}
type PetType = Bird | Fish;
interface IPet extends PetType {} // error
class Pet implements PetType {} // error
interface Person {
name: string;
}
interface Person {
age: number;
}
let person: Person = {
name: 'gugu',
age: 12
}
console.log(person)
const foo1: (a: string, b: number) => string = (a, b) => {
return a;
};
const foo2 = (a: string, b: number): string => {
return a;
};
const foo3: (a: string, b: number) => string = function (a, b) {
return a;
};
const foo4 = function (a: string, b: number): string {
return a;
}
function foo5(a: string, b: number): string {
return a;
}
function addString(x: string, y: string): string {
console.log(x, y);
return x + y;
}
function addNumber(x: number, y: number): number {
console.log(x, y);
return x + y;
}
function add(x: string, y: string): string; // 구현부
function add(x: number, y: number): number; // 구현부
function add(x: any, y: any): any { // 선언부
console.log(x, y);
return x + y;
}
add('Hi', 'World');
add(12, 13);
add('Hi', 13); // error
add(12, 'World'); // error
interface UserBase {
name: string;
age: number;
}
interface User extends UserBase {
updateInfo(newUser: UserBase): User;
updateInfo(name: string, age: number): User;
}
const user: User = {
name: 'gu',
age: 24,
updateInfo: function (nameOrUser: UserBase | string, age?: number) {
if (typeof nameOrUser === 'string' && age !== undefined) {
this.name = nameOrUser;
this.age = age;
} else if (typeof nameOrUser === 'object') {
this.name = nameOrUser.name;
this.age = nameOrUser.age;
}
return this;
},
};
console.log(user.updateInfo({ name: 'gu', age: 23 })); // { name: 'gu', age: 23, updateInfo: f }
console.log(user.updateInfo('Leon', 51)); // { name: 'Leon', age: 51, updateInfo: f }
class PersonClass {
public constructor(public name: string, public age: number) {}
//위의 코드는 아래의 코드와 동일한 결과를 보여줌
public name: string;
public age: number;
public constructor(name: string, age: number){
this.name = name;
this.age = age;
}
}
const p4 = new PersonClass('Gu', 13);
console.log(p4);
// { gu: 'male', mark: 'male, jade: 'male'}
// { gu: 'male', chloe: 'female', alex: 'male', anna: 'female'}
class Students {
[index: string]: "male" | "female";
gu: 'male' = 'male';
}
const a = new Students();
a.mark = 'male';
a.jade = 'male';
console.log(a);
const b = new Students();
b.chloe = 'female';
b.alex = 'male';
b.anna = 'female';
console.log(b);
class PersonClass {
private static CITY = 'SEOUL';
public static hello() {
console.log('hi', PersonClass.CITY);
}
}
const p = new PersonClass();
// p.hello();
PersonClass.hello(); //hi SEOUL
class PersonClass {
private static CITY = 'SEOUL';
public hello() {
console.log('hi', PersonClass.CITY);
}
public change() {
PersonClass.CITY = 'LA';
}
}
const p = new PersonClass();
p.hello(); // hi SEOUL
const p12 = new PersonClass();
p12.hello(); // hi SEOUL
p.change();
p12.hello(); // hi LA
private constructor를 사용해 new 키워드로 클래스에 대한 오브젝트 생성을 막는다.
오브젝트를 처음 생성하거나 생성된 오브젝트가 이미 존재한다면 그걸 리턴하는 메서드를 만든다.
class ClassName {
private static instance: ClassName | null = null;
public static getInstance(): ClassName {
// ClassName으로부터 만든 object가 있으면 그걸 리턴
// 없으면, 만들어서 리턴
if (ClassName.instance === null) {
ClassName.instance = new ClassName();
}
return ClassName.instance;
}
private constructor() {}
}
const a = ClassName.getInstance();
const b = ClassName.getInstance();
console.log(a === b); // true
abstract class AbstractPerson {
abstract name: string;
abstract getName(): string;
}
// new AbstractPerson(); 에러 발생
class PersonClass extends AbstractPerson {
constructor(public name: string) {
super();
}
getName(): string {
return this.name;
}
}
const p = new PersonClass('GuGuGu');
console.log(p.getName());
class Cat extends CatA implements AnimalI1, AnimalI2 {
...
}
abstract class AnimalA {
abstract color: string;
abstract getColor(): string;
constructor(public sound: string) {}
speak() {
return this.sound;
}
}
class Dog extends AnimalA {
constructor(
sound: string,
public color: string,
) {
super(sound);
}
getColor() {
return this.color;
}
}
class Cat implements Animal1, Animal2 {
...
}
function deco(target: any) {
console.log(target);
return class {
constructor(
public a: any,
public b: any,
) {
console.log(this.a, this.b);
}
} as any;
}
@deco // 해당 부분에서 User클래스는 자신의 클래스 구조가 아닌
// deco 함수가 반환한 클래스의 구조를 가지게됨
class User {
constructor(public name: string) {}
hello(msg: string) {
return `Hello ${this.name}, ${msg}`;
}
}
const gu = new User('Gu', 21); // deco 함수의 반환값인 클래스로 객체 생성
const neo = new User('Neo', 22); // deco 함수의 반환값인 클래스로 객체 생성
console.log(gu);
console.log(neo);
function deco<T extends { new (...args: any[]): any }>(target: T) {
if (target.name !== 'User') {
throw new Error('클래스의 이름이 User가 아님');
}
console.log('정상적인 클래스의 이름');
// 반환하는 클래스의 연결된 클래스를 확장해서 복사함
return class extends target {
constructor(...args: any[]) {
super(args);
fetch('서버 주소', {
//
});
console.log('서버로 데이터를 보냄');
}
};
}
@deco // 해당 부분에서 User클래스는 자신의 클래스 구조가 아닌
// deco 함수가 반환한 클래스의 구조를 가지게됨
class User {
constructor(public name: string) {}
hello(msg: string) {
return `Hello ${this.name}, ${msg}`;
}
}
const gu = new User('Gu'); // deco 함수의 반환값인 클래스로 객체 생성
const neo = new User('Neo'); // deco 함수의 반환값인 클래스로 객체 생성
console.log(gu);
console.log(neo);
Any는 input에 따라 달라지는 연산이 불가능하지만 Generic은 가능함
function hello(message: any): any {
return message;
}
console.log(hello('mark').length);
console.log(hello(13).length); //오류가 발생하지 않음
function helloGenric<T>(message: T): T {
return message;
}
console.log(helloGenric('mark').length);
console.log(helloGenric(13).length); //오류 발생
function helloBasic<T, U>(message: T, commet: U): T {
return message;
}
helloBasic<string, number>("dd", 12); //way 1 타입 직접 지정
helloBasic(23, 21); //way 2 타입 추론
interface ToObj<T> {
a: T;
b: T;
}
function toObj<T extends string | number>(a: T, b: T): ToObj<T> {
return { a, b };
}
toObj<string>('A', 'B');
toObj<number>(1, 2);
toObj<boolean>(true, false); // 에러 발생
function helloArray<T>(message: T[]): T {
return message[0];
}
helloArray(['hello', 'world']);
helloArray(["hello", 4]); //string|number union타입으로 추론
function helloTuple<T, K>(message: [T, K]): T {
return message[0];
}
helloTuple(["hello", "world"]);
helloTuple(["hello", 1]);
type HelloFunctionGeneric = <T>(message: T) => T;
const helloFunction1: HelloFunctionGeneric = <T>(message: T): T => {
return message;
}
interface HelloFunctionGeneric2 {
<T>(message: T): T;
}
const helloFunction2: HelloFunctionGeneric2 = <T>(message: T): T => {
return message;
}
class Person<T, K> {
private _name: T;
private _age: K;
constructor(name: T, age: K) { // constructor에서 T, K가 정의됨
this._name = name;
this._age = age;
}
}
new Person("Gu", 12);
new Person<string, number>('gu', 23);
generic에서 extends는 extends로 정해준 타입만 가능하다는 뜻
class PersonExtends<T extends string | number> {
private _name: T;
constructor(name: T) {
this._name = name;
}
}
new PersonExtends('mark');
new PersonExtends(123);
new PersonExtends(true); //오류 발생
type MyType<T> = T extends string | number ? boolean : never;
const a: MyType<string> = true; // MyType이 boolean이 됨
const b: MyType<number> = true; // MyType이 boolean이 됨
const c: MyType<null> = true; ; // MyType이 never가 되므로 에러 발생
type MyExclude<T, U> = T extends U ? never : T;
type MyUnion = string | number | boolean | null;
const a: MyExclude<MyUnion, boolean | null> = 123; // a라는 변수는 string이거나 number이여야할 때 사용함
// string, number를 제외한 나머지 타입은 never가 됨
// TS는 이미 내장된 Exclude 타입이 존재함
const a: Exclude<MyUnion, boolean | null> = 123;
interface IPerson {
name: string;
age: number;
}
const person: IPerson = {
name: 'Gu',
age: 12,
};
// IPerson[keyof IPerson]
// => IPerson['name' | 'age']
// => IPerson['name'] | IPerson['age']
// => string | number
function getProp<T, K extends keyof T>(obj: T, key: K): T[K] {
return obj[key];
}
getProp(person, 'name');
function setProp<T, K extends keyof T>(
obj: T,
key: K,
value: T[K]
): void {
obj[key] = value;
}
setProp(person, 'name', 'Mark');
조건부 타입에서 타입 변수를 추론할 때 사용하는 키워드
type ArrayItemType<T> = T extends (infer I)[] ? I : never;
const numbers = [1, 2, 3];
const a: ArrayItemType<typeof numbers> = 123; // I가 number타입이 됨
const b: ArrayItemType<boolean> = 123; // 에러 발생
const fruits = ['apple', 'banana', 'melon'];
const hello = () => {};
const c: ArrayItemType<typeof fruits> = 'ABC'; // I가 string타입이 됨
const d: ArrayItemType<typeof hello> = 'ABC' // 에러 발생
type SecondArgumentType<T> = T extends (f: any, s: infer S) => any ? S : never;
function hello(a: string, b: number) {}
const a: SecondArgumentType<typeof hello> = 123;
type MyReturnType<T extends (...args: any) => any> = T extends (
...args: any
) => infer R
? R
: any;
function add(x: string, y: string) {
return x + y;
}
const a: MyReturnType<typeof add> = 'hello';
// ReturnType이라는 내장 유틸리티 타입이 있음
const a: ReturnType<typeof add> = 'hello';
export namespace Utils {
export interface Add {
// namespace 안에서도 export 키워드가 필요함
(a: number, b: number): number;
}
export interface Subtract {
(a: number, b: number): number;
}
}
export const add: Utils.Add = (a, b) => a + b;
export const subtract: Utils.Subtract = (a, b) => a - b;
export default {
name: 'My utils!',
add,
subtract,
};
import utils, { add, subtract } from './myUtils'; // // 데이터로 취급
import type { Utils } from './myUtils'; // 타입으로 취급
// import { add, subtract, Add, Subtract } from './myUtils';
// 데이터와 타입을 섞어서 import하는 것도 가능하지만 직관적으로 구분해서 표현하는 것이 좋음
const a = add(4, 7);
const b = subtract(9, 6);
const c = utils.add(4, 7);
const d = utils.subtract(9, 6);
console.log(utils.name); // 'My utils!'
const newAdd: Utils.Add = (x, y) => x + y;
newAdd(1, 2);
const newSubtract: Utils.Subtract = (x, y) => x - y;
newSubtract(3, 1);
{
...,
"compileOnSaveDefinition": {
"properties": {
"compileOnSave": {
//save하면 컴파일을 활성화함
"description": "Enable Compile-on-Save for this project.",
"type": "boolean"
}
}
},
...,
}
//따라서 아래와 같이 지정하면 파일을 save하면 자동 컴파일이 됨
{
"compileOnSave": true,
}
ts파일을 js파일로 변환할 때 어떤 옵션을 사용해서 해석할 것인지 지정
엄격한 타입 검사를 활성화 하는 옵션, 아래의 옵션들을 자동으로 활성화함(기본 값: false)
명시적이지 않게 any타입을 사용하여, 표현식과 선어에 사용하면 에러 발생
명시적이지 않게 any타입을 사용하여 this 표현식에 사용하면 에러를 발생
null 및 undefined 값이 모든 유형의 도메인에 속하지 않으며, 그 자신을 타입으로 가지거나, any일 겨우에만 할당이 가능
strictNullChecks를 적용하지 않으면 모든 타입은 null, undefined 값을 가질 수 있음. (string으로 타입을 지정해도 null, undefined값을 할당할 수 있음)
엄격한 함수의 매개변수 타입 검사
정의되지 않은 클래스의 속성이 생성자에서 초기화되었는지 확인
이 옵션을 사용하려면 --strictNullChecks를 사용하도록 설정해야함
Function의 내장 함수인 bind/call/apply를 사용할 때, 엄격하게 체크하도록 하는 옵션
각 소스파일에 대해 JS의 strict mode로 코드를 분석하고, “엄격하게 사용”을 해제
// tsconfig.js
{
"compilerOptions": {
"paths": {
"myUtils": ["./my_module/utils.ts"],
"~/*": ["./src/*"]
}
},
}
// main.ts
import { add } from 'myUtils';
import { add } from '~/my_modules/utils' // === ./src/my_modules/utils
interface User {
name: string,
age: number,
}
// error
const userA: User = {
name: 'A'
}
// 에러가 발생하지 않음
const userB: Partial<User> = {
name: 'B'
}
interface User {
name?: string,
age?: number,
}
// 에러가 발생하지 않음
const userA: User = {
name: 'A'
}
// error
const userB: Required<User> = {
name: 'B'
}
interface User {
name?: string;
age?: number;
}
const userA: User = {
name: 'A',
age: 12,
};
const userB: Readonly<User> = {
name: 'B',
age: 12,
};
userA.name = 'AA';
userB.name = 'BB'; // error
type Names = 'neo' | 'gugu';
const developers: Record<Names, number> = {
neo: 12,
gugu: 13,
}
// Record<Names, number>를 통해 만들어지는 타입은 RecordNames와 동일함
type RecordNames = {
neo: number
gugu: number
}
interface User {
name: string;
age: number;
email: string;
}
// Pick<User, 'name' | 'email'>의 결과는 PickUser와 동일함
const user: Pick<User, 'name' | 'email'> = {
name: 'gugu',
email: 'gugu@gugu.com',
age: 11, // error
};
interface PickUser {
name: string;
email: string;
}
interface User {
name: string;
age: number;
email: string;
}
// Omit<User, 'name' | 'email'>의 결과는 OmitUser와 동일함
const user: Omit<User, 'name' | 'email'> = {
age: 11,
};
interface OmitUser {
age: number
}
type T = string | number | boolean;
const a: Exclude<T, number> = 'string';
const b: Exclude<T, number> = 123; // 에러 발생
type T = string | number | boolean;
type U = number | boolean | string[];
const a: Extract<T, U> = 123
// Extract<T, U> === number | boolean
어떤 타입이 반환되는지 알아냄
function hello(msg: string) {
return msg;
}
const a: ReturnType<typeof hello> = 'string'
(async () => {
const promise = Promise.resolve(true);
console.log(await promise); // true
const a: Awaited<typeof promise> = false;
})();
굉장히 정리가 잘 되어있네요!!
잘 참고해서 사용해보겠습니다 👍