type T0 = Exclude<"a" | "b" | "c", "a">;
// type T0 = "b" | "c"
type T1 = Exclude<"a" | "b" | "c", "a" | "b">;
// type T1 = "c"
type T2 = Exclude<string | number | (() => void), Function>;
// type T2 = string | number
UnionType에 할당된 것에 제외할 멤버를 제외하고 Type 형식을 구성합니다
📝Extract<Type, Union>
type T0 = Extract<"a" | "b" | "c", "a" | "f">;
// type T0 = "a"
type T1 = Extract<string | number | (() => void), Function>;
// type T1 = () => void
Type과 Union의 교집합에 해당하는 부분으로 Type형식을 구성합니다
📝NonNullable<Type>
type T0 = NonNullable<string | number | undefined>;
// type T0 = string | number
type T1 = NonNullable<string[] | null | undefined>;
// type T1 = string[]
null및 undefined를 제외하여 유형을 구성합니다
📝Parameters<Type>
function greet(name: string, age: number): string {
return `Hello, ${name}. You are ${age} years old.`;
}
type GreetParameters = Parameters<typeof greet>;
// GreetParameters 타입은 [string, number]와 동일합니다.
const user: GreetParameters = ["John Doe", 30];
유틸리티 타입은 함수의 매개변수 타입들을 튜플 타입으로 추출합니다
📝ConstructorParameters
class User {
constructor(public name: string, public age: number) {}
}
// User 생성자의 매개변수 타입을 추출합니다.
type UserConstructorParameters = ConstructorParameters<typeof User>;
// UserConstructorParameters는 [string, number] 타입과 동일합니다.
const userParams: UserConstructorParameters = ["Jane Doe", 32];
클래스 생성자의 매개변수 타입들을 튜플로 추출합니다
📝ReturnType<Type>
function getUser() {
return { name: "John Doe", age: 30 };
}
// getUser 함수의 반환 타입을 추출합니다.
type User = ReturnType<typeof getUser>;
// User 타입은 { name: string; age: number; }와 동일합니다.
const user: User = { name: "Jane Doe", age: 32 };
type A = Awaited<Promise<string>>;
// type A = string
type B = Awaited<Promise<Promise<number>>>;
// type B = number
type C = Awaited<boolean | Promise<number>>;
// type C = number | boolean
interface Props {
a?: number;
b?: string;
}
const obj: Props = { a: 5 };
const obj2: Required<Props> = { a: 5 };
// Property 'b' is missing in type '{ a: number; }' but required in type 'Required<Props>'.
해당 필드에 내용이 반드시 필요하다는 선언이 가능합니다
📝Readonly<Type>
interface Todo {
title: string;
}
const todo: Readonly<Todo> = {
title: "Delete inactive users",
};
todo.title = "Hello";
Cannot assign to 'title' because it is a read-only property.
interface Person {
name: string;
age: number;
}
type NameType = Person["name"]; // string
type AgeType = Person["age"]; // number
const age: AgeType = 50
const nm: NameType = "Emily"
index라는 걸 이용해 해당 interface의 필요 타입을 발췌할 수 있다
📝조건부 유형
/** 기본 형태 **/
type IsStringType<T> = T extends string ? string[] : number[];
type T1 = IsStringType<string>; // type T1 = string[]
type T2 = IsStringType<number>; // type T2 = number[]
const a: T1 = ['홍길동', '임꺾정', '박혁거세'];
const b: T2 = [1000, 2000, 3000];
/** 유니온 타입인 경우 **/
type ToArray<Type> = Type extends any ? Type[] : never;
type StrArrOrNumArr = ToArray<string | number>;
// type StrArrOrNumArr = string[] | number[]
기본형태의 경우 제너릭 T가 string을 상속받을 수 있다면 string[]이 선언되고 그렇지 않으면 number[]가 선언된다
유니온의 타입의 경우 유니온 타입으로 생성된다
📝템플릿 리터럴형
type World = "world";
type Greeting = `hello ${World}`;
// type Greeting = "hello world"
type EmailLocaleIDs = "welcome_email" | "email_heading";
type FooterLocaleIDs = "footer_title" | "footer_sendoff";
type AllLocaleIDs = `${EmailLocaleIDs | FooterLocaleIDs}_id`;
// type AllLocaleIDs = "welcome_email_id" | "email_heading_id" | "footer_title_id" | "footer_sendoff_id"
자바스크립트 이벤트 바인딩 on 재선언해서 타입스크립트 적용시키는 활용 예제
type PropEventSource<Type> = {
on(eventName: `${string & keyof Type}Changed`, callback: (newValue: any) => void): void;
};
/// Create a "watched object" with an `on` method
/// so that you can watch for changes to properties.
declare function makeWatchedObject<Type>(obj: Type): Type & PropEventSource<Type>;
📝리터럴 타입 조작 유형
type Greeting = "Hello, world"
type ShoutyGreeting = Uppercase<Greeting>
// type ShoutyGreeting = "HELLO, WORLD"
type ASCIICacheKey<Str extends string> = `ID-${Uppercase<Str>}`
type MainID = ASCIICacheKey<"my_app">
// type MainID = "ID-MY_APP"
리터럴 타입을 조작할 수 있고 다양한 옵션들이 존재한다
📝Flatten
const a = {
a : "key 'a'",
b : {
aa : "key 'b.aa'",
bb : "key 'b.bb'",
},
c : {
aa : {
aaa : "key 'c.aa.aaa'",
bbb : "key 'c.aa.bbb'",
}
},
}
const a = {
"a" : "key 'a'",
"b.aa" : "key 'b.aa'",
"b.bb" : "key 'b.bb'",
"c.aa.aaa" : "key 'c.aa.aaa'"
"c.aa.bbb" : "key 'c.aa.bbb'"
}
Flatten이란 깊이가 1 이상인 object들을 일정한 키 생성 규칙에 따라 깊이가 1로 고정된 오브젝트로 전환하는 기능을 말한다
만약에라이브러리에 타입스크립트가 적용 안 된 라이브러리라면 어떻게 해야할까요? 타입스크립트로 프로젝트를 만든 경우 타입이 지정되지 않아 정상 동작하지 않게 되는 문제가 생기게 됩니다 이러한 경우 서드파트 및 라이브러리에 대한 타입선언을 우리가 직접적으로 해줘야합니다 이러한 선언들은 보통 .d.ts파일에다가 관리를 합니다
📝Flatten
const a = {
a : "key 'a'",
b : {
aa : "key 'b.aa'",
bb : "key 'b.bb'",
},
c : {
aa : {
aaa : "key 'c.aa.aaa'",
bbb : "key 'c.aa.bbb'",
}
},
}
const a = {
"a" : "key 'a'",
"b.aa" : "key 'b.aa'",
"b.bb" : "key 'b.bb'",
"c.aa.aaa" : "key 'c.aa.aaa'"
"c.aa.bbb" : "key 'c.aa.bbb'"
}
깊이가 1 이상인 object들을 일정한 키 생성 규칙에 따라 깊이가 1로 고정된 오브젝트로 전환하는 기능
function liveDangerously(x?: number | null) {
// No error
console.log(x!.toFixed());
}
/** ---- function ---- **/
function hello(name?: string, age?: number, hobby?: string | undefined){
return `Hello, ${name}. You are ${age}. and My Hobby is ${hobby}`;
}
console.log(hello("Lee", 30)); // Hello, Lee. You are 30. and My Hobby is undefined
// 예시2
// void 리턴인 경우 리턴 값이 있을 수도 없을 수도 있다
type voidFunc = () => void;
const f1: voidFunc = () => {
return true;
};
/** ---- Function ---- **/
// Function은 함수계의 any이다
function doSomething(f: Function) {
return f(1, 2, 3);
}
/** ---- 유니온 타입 ---- **/
function printId(id: number | string) {
console.log("Your ID is: " + id);
}
printId(101); // OK
printId("202"); // OK
두개의 타입을 허용합니다
📝익명 함수
/** ---- 익명 함수 ---- **/
interface Minus {
(num1 : number, num2 : number):number;
}
const minus: Minus = (x, y) => {
return x - y;
}
console.log(minus(1,5)); // -4
📝Interface
/** ---- interface ---- **/
// 인터페이스는 ES6가 지원하지 않는 타입스크립트만의 특징으로 JS컴파일 후에 사라집니다.
interface Car {
color: string;
wheels: number;
start() : void;
}
/** ---- interface extend interface ---- **/
// interface끼리 확장은 가능하지만 Java처럼 Interface 그 자체로는 사용을 못한다
interface Stuff {
color: string
}
interface Mouse extends Stuff {
name: string
}
📝Class
/** ---- class extends class ---- **/
class Cup {
color: string;
constructor(color: string) {
this.color = color;
}
sayMyName(): void{
console.log(this.color);
}
}
class GlassCup extends Cup{
name: string;
constructor(color: string, name: string) {
super(color);
this.name = name;
}
sayMyName() {
console.log("glassCup");
}
}
let glassCup = new GlassCup("blue","france cup")
console.log(`glassCup.name : ${glassCup.name}`) // glassCup.name : france cup
glassCup.sayMyName(); // glassCup
클래스의 경우 constructor가 필요하다 그렇지 않으면 에러 발생한다
this의 경우 클래스 안의 변수를 찾아간다 this를 사용하지 않으면 class 밖에 있는 변수를 찾아간다
Getter, Setter
class C {
_length = 0;
get length() {
return this._length;
}
set length(value) {
this._length = value;
}
}
getter와 setter를 선언할 수 있다
오버라이드
class Base {
greet() {
console.log("Hello, world!");
}
}
class Derived extends Base {
greet(name?: string) {
if (name === undefined) {
super.greet();
} else {
console.log(`Hello, ${name.toUpperCase()}`);
}
}
}
const d = new Derived();
d.greet(); // Hello, world!
d.greet("reader"); // Hello, READER
오버라이드할 때 상위 클래스의 형태를 포함하고 있지 않으면 에러가 발생한다 위의 코드의 경우는 optional처리가 되어있어서 상관 없지만 그렇지 않으면 에러 발생합니다
public, protected, private, static
/** public **/
class Greeter {
public greet() {
console.log("hi!");
}
}
const g = new Greeter();
g.greet();
/** protected **/
class Greeter {
public greet() {
console.log("Hello, " + this.getName());
}
protected getName() {
return "hi";
}
}
class SpecialGreeter extends Greeter {
public howdy() {
// OK to access protected member here
console.log("Howdy, " + this.getName());
}
}
const g = new SpecialGreeter();
g.greet(); // OK
g.getName();
// Property 'getName' is protected and only accessible within class 'Greeter' and its subclasses
/** private **/
class Base {
private x = 0;
}
const b = new Base();
// Can't access from outside the class
console.log(b.x);
// Property 'x' is private and only accessible within class 'Base'.
/** static **/
class MyClass {
static x = 0;
static printX() {
console.log(MyClass.x);
}
}
console.log(MyClass.x);
MyClass.printX();
public의 경우 외부에서 코드를 제어할 수 있습니다
protected는 외부에서 코드를 제어할 수 없고 extends했을 때는 해당 클래스 내부에서는 사용이 가능합니다
private은 protected랑 비슷하지만 하위 클래스에서도 멤버에 대한 엑세스 허용이 되지 않습니다
static의 경우는 클래스의 객체를 만들어서 쓰는게 아니라 클래스 자체를 수정할 수 있습니다
📝Abastract Class
class Derived extends Base {
getName() {
return "world";
}
}
const d = new Derived();
d.printName();
class Derived extends Base {
//Non-abstract class 'Derived' does not implement inherited abstract member 'getName' from class 'Base'.
// forgot to do anything
}
추상 클래스의 경우 상속받아서 사용하며 추상 메서드는 구현이 필요하다
📝Class Implements Interface
/** ---- class implements interface ---- **/
class BMW implements Car{
color;
wheels = 4;
constructor(color: string) {
this.color = color;
}
start(){
console.log("go....");
}
}
const bmw = new BMW('green');
bmw.start(); // go....
console.log(bmw); // BMW { wheels : 4, color : 'green' }
/** ---- class implements interface(2) ---- **/
interface A {
x: number;
y?: number;
}
class C implements A {
x = 0;
}
const c = new C();
c.y = 10;
// Property 'y' does not exist on type 'C'.
인터페이스를 상속받을 때 Optional인 경우 해당 속성은 생성되지 않습니다
📝Type
/** ---- Type ---- **/
type Animal = {
name: string;
}
/** ---- Type Extension ---- **/
type Bear = Animal & {
honey: boolean;
}
const bear = getBear();
bear.name;
bear.honey;
/** ---- Type vs Interface ---- **/
// interface 기능을 Type이 대부분 사용 가능하지만 Type은 재선언이 불가능
interface Window {
title: string;
}
interface Window {
ts: TypeScriptAPI;
}
type Window = {
title: string;
}
type Window = {
ts: TypeScriptAPI;
}
// Error: Duplicate identifier 'Window'.