type Config = {
path : string;
state : object;
}
type Push = {
(path:string):void;
(config: Config):void;
}
const push: Push = (config) => { // (parameter) config: string | Config
}
TypeScript에서 config가 (parameter) config: string | Config로 추론되는 이유:
Push 타입이 함수 오버로딩으로 정의되어 있기 때문
함수 오버로딩은 여러 가지 호출 시그니처(Signature)를 사용하여 동일한 함수 이름을 다양한 형태로 호출할 수 있도록 만드는 기능입니다. TypeScript는 오버로드된 함수를 정의할 때, 여러 개의 함수 시그니처를 기반으로 인자의 타입을 추론합니다.
Push 타입을 보면 다음과 같이 두 가지 호출 시그니처를 가지고 있습니다:
type Push = {
(path: string): void; // 첫 번째 시그니처
(config: Config): void; // 두 번째 시그니처
};
string 타입의 path만 받을 수 있습니다.Config 타입({ path: string, state: object })의 config 객체를 받을 수 있습니다.이 두 시그니처를 합치면, TypeScript는 해당 함수 push가 다음과 같은 형태로 호출될 수 있다고 추론합니다:
push("somePath")처럼 문자열을 인자로 받을 수 있고,push({ path: "somePath", state: {} })처럼 객체를 인자로 받을 수도 있습니다.string | Config로 추론되는가?TypeScript는 함수의 구현부(함수 본문)에서 전달된 인자의 타입을 보고 자동으로 가장 포괄적인 타입으로 추론하려고 합니다.
const push: Push = (config) => {};
위 코드를 살펴보면, push 함수는 Push 타입을 구현하고 있습니다. Push 타입은 string과 Config라는 두 가지 시그니처를 가지고 있기 때문에, TypeScript는 다음과 같이 자동으로 config의 타입을 string | Config로 추론하게 됩니다:
const push: Push = (config: string | Config) => {};
즉, config가 첫 번째 시그니처의 인자인 string이 될 수도 있고, 두 번째 시그니처의 인자인 Config 객체가 될 수도 있다는 뜻입니다.
다음 예제를 통해 함수 오버로딩과 타입 추론이 어떻게 이루어지는지 확인해 보겠습니다:
type Config = {
path: string;
state: object;
};
type Push = {
(path: string): void; // 첫 번째 시그니처
(config: Config): void; // 두 번째 시그니처
};
const push: Push = (config) => {
console.log(config);
};
위 코드에서 config는 string일 수도 있고 Config 타입일 수도 있습니다. 따라서 TypeScript는 config를 string | Config 타입으로 추론합니다. 이렇게 추론이 되면 함수 내부에서 config의 타입을 확실하게 구분하지 않으면 path나 state에 접근할 때 타입 오류가 발생할 수 있습니다.
함수 내부에서 string과 Config 타입을 명확히 구분하기 위해 타입 가드를 사용할 수 있습니다. 예를 들어:
const push: Push = (config) => {
if (typeof config === "string") {
// config가 문자열일 때 실행할 로직
console.log("String path:", config);
} else {
// config가 Config 객체일 때 실행할 로직
console.log("Config object:", config.path, config.state);
}
};
위와 같이 typeof를 사용하여 config가 문자열인지 객체인지 확인하면 타입 오류 없이 안전하게 접근할 수 있습니다.
string | Config)을 추론합니다.Push 타입은 (path: string): void와 (config: Config): void 두 가지 시그니처를 가지고 있으므로, TypeScript는 config의 타입을 string | Config로 추론합니다.string과 Config를 구분하면 더욱 안전하게 코드를 작성할 수 있습니다.아래 두 함수 합쳐놓은 것
function superPrint<T>(a: T[]){
return a[0]
}
제네릭 기본 사용 방법
type SuperPrint = {
<T>(arr: T[]): T
}
const superPrint: SuperPrint = (arr) => arr[0]
type Player<E> = {
name:string
extraInfo: E
}
const nico : Player<{favFood:string}> = {
name: "nico",
extraInfo: {
favFood: "kimchi"
}
}
// 위 코드를 둘로 나눈것 1
type NicoPlayer = Player<{favFood:string}>
const nico : NicoPlayer = {
name: "nico",
extraInfo: {
favFood: "kimchi"
}
}
// 위 코드를 둘로 나눈것 2
type NicoPlayer = Player<NicoExtra>
type NicoExtra = {
favFood: string
}
const nico : NicoPlayer = {
name: "nico",
extraInfo: {
favFood: "kimchi"
}
}
const lynn : Player<null> = {
name: "lynn",
extraInfo: null
}
class Player {
constructor(
private firstName: string,
private lastName: string
) {}
}
실제 javascript에서는
"use strict"; class Player { constructor(firstName, lastName){ this.firstName = firstName; this.lastName = lastName; } }
추상클래스 : 다른 클래스가 상속받을 수 있는 클래스 but 직접 새로운 인스턴스를 만들 수는 없다.
abstract class User{
constructor(
private firstName: string,
private lastName: string,
public nickName: string
) {}
getFullName(){
return `${this.firstName} ${this.lastName}`
}
}
class Player extends User{
}
const nico = new Player("nico" , "las", "니꼬");
nico.getFullName()
타입 사용방법1
type Player1 = {
nickname: string,
healthBar: number
}
const nico1 : Player1 = {
nickname: "nico1",
healthBar: 10
}
타입 사용방법2
type Food = string;
const kimchi : Food = "delicious"
타입 사용방법3
type Nickname = string
type Health = number
type Friends = Array<string> // type Friends = string[]
type Player2 = {
nickname: Nickname,
healthBar: Health
}
const nico2 : Player2 = {
nickname: "nico2",
healthBar: 10
}