Typescript: More On Functions

@Troy Β· October 23, 2023 Β· 13 min read

More On FunctionsπŸ˜ƒ

νƒ€μž…μŠ€ν¬λ¦½νŠΈμ—μ„œ ν•¨μˆ˜λ₯Ό λ‹€λ£¨λŠ” 법을 μ•Œμ•„λ³΄μž

Function Type Expressions

νƒ€μž…μŠ€ν¬λ¦½νŠΈμ—μ„œ fuction을 ν‘œν˜„ν•  λ•Œ 기본적으둜 parameter와 return 값에 λŒ€ν•΄ νƒ€μž…μœΌλ‘œ ν‘œν˜„ν•œλ‹€. Parameter type의 경우 νƒ€μž…μ„ 정해주지 μ•ŠμœΌλ©΄ any둜 정해진닀.

function greeter(fn: (a: string) => void) {
  fn("Hello, World");
}
 
function printToConsole(s: string) {
  console.log(s);
}
 
greeter(printToConsole);

Call Signatures

μžλ°”μŠ€ν¬λ¦½νŠΈμ—μ„œ ν•¨μˆ˜λŠ” 객체닀. 이점이 μ€‘μš”ν•œ 점은 ν•¨μˆ˜κ°€ 속성을 κ°€μ§ˆ 수 μžˆλ‹€λŠ” 의미이기 λ•Œλ¬ΈμΈλ°, function type annotationμ—μ„œλŠ” 속성을 ν• λ‹Ήν•˜μ§€ λͺ»ν•˜κ²Œ ν•œλ‹€. 속성을 ν• λ‹Ήν•˜κ³  μ‹Άλ‹€λ©΄ ν•΄λ‹Ή 속성에 λŒ€ν•œ call signatureλ₯Ό ν• λ‹Ήν•΄μ•Ό ν•œλ‹€.

type DescribableFunction = {
  description: string;
  (someArg: number): boolean;
};
function doSomething(fn: DescribableFunction) {
  console.log(fn.description + " returned " + fn(6));
}

function myFunc(someArg: number) {
  return someArg > 3;
}
myFunc.description = "default description";

doSomething(myFunc);

Construct Signatures

μžλ°”μŠ€ν¬λ¦½νŠΈμ—μ„œ ν•¨μˆ˜ μ„ μ–Έλ¬ΈμœΌλ‘œ μ •μ˜λœ ν•¨μˆ˜λŠ” λͺ¨λ‘ μƒμ„±μž ν•¨μˆ˜λ‘œ 쓰일 수 있기 λ•Œλ¬Έμ— new ν‚€μ›Œλ“œκ°€ μ‚¬μš©λ  수 μžˆλ‹€.newν‚€μ›Œλ“œλ₯Ό μ΄μš©ν•œ νƒ€μž… μ •μ˜λ₯Ό 톡해 construct signatureλ₯Ό μ •μ˜ν•  수 μžˆλ‹€.

type SomeConstructor = {
  new (s: string): SomeObject;
};
function fn(ctor: SomeConstructor) {
  return new ctor("hello");
}

Generic Functions

보톡 param의 νƒ€μž…κ³Ό λ°˜ν™˜κ°’μ˜ νƒ€μž…μ€ 관련이 μžˆκ±°λ‚˜ paramλ“€κ°„μ˜ νƒ€μž…μ΄ 관계가 μžˆλ‹€. anyλ₯Ό μ‚¬μš©ν•˜μ§€ μ•Šκ³  μš°λ¦¬κ°€ μ›ν•˜λŠ” νƒ€μž…μœΌλ‘œ μ’ν˜€μ„œ μž¬μ‚¬μš©ν•˜λŠ” λ°©λ²•μœΌλ‘œ generic을 μ΄μš©ν•  수 μžˆλ‹€.

function firstElement<Type>(arr: Type[]): Type | undefined {
  return arr[0];
}

// s is of type 'string'
const s = firstElement(["a", "b", "c"]);
// n is of type 'number'
const n = firstElement([1, 2, 3]);
// u is of type undefined
const u = firstElement([]);

inference

inferenceλŠ” νƒ€μž…μ„ 직접 μ •ν•˜λŠ” 것은 μ•„λ‹ˆμ§€λ§Œ typescriptλ₯Ό 톡해 μΆ”λ‘ λ˜λŠ” νƒ€μž…μ„ μ˜λ―Έν•œλ‹€.

function map<Input, Output>(arr: Input[], func: (arg: Input) => Output): Output[] {
  return arr.map(func);
}
 
const parsed = map(["1", "2", "3"], (n) => parseInt(n)); // parsedsms number[]이야

Constraints와 였λ₯˜

generic을 μ΄μš©ν•΄μ„œ μ—°κ΄€μžˆλŠ” input νƒ€μž…λ“€μ˜ 관계λ₯Ό μ •μ˜ν–ˆμ§€λ§Œ μ–΄λŠμ •λ„ μš°λ¦¬κ°€ μ›ν•˜λŠ” νƒ€μž…μœΌλ‘œ 쒁힐 수 μžˆλ‹€. 쒁힐 λ•ŒλŠ” extendsλ₯Ό 톡해 ν•΄λ‹Ή 속성을 가지고 μžˆλŠ” νƒ€μž… λ“±μœΌλ‘œ 쒁힐 수 μžˆλ‹€.

constraint
constraint

이런 Generic을 μ΄μš©ν•œ νƒ€μž… μ’νžˆκΈ°λŠ” μœ μš©ν•  수 μžˆμ§€λ§Œ 쑰심해야할 뢀뢄이 μžˆλ‹€.

constraint
constraint

μœ„ μ½”λ“œμ—μ„œ μ—λŸ¬κ°€ λ‚œ μ΄μœ λŠ” 우리 Generic으둜 μ „λ‹¬ν•œ 인자의 νƒ€μž…μ΄ λ°˜ν™˜κ°’μœΌλ‘œ κ·ΈλŒ€λ‘œ λ°˜ν™˜λ  것라고 μ„ μ–Έν–ˆλ‹€. ν•˜μ§€λ§Œ elseλ¬Έ μ•ˆμ—λŠ” μ’ν˜€μ§„ λ²”μœ„μ˜ 객체λ₯Ό λ°˜ν™˜ν•˜κΈ° λ•Œλ¬Έμ— κΈ°μ‘΄κ³Ό λ‹€λ₯Έ νƒ€μž…μ΄κΈ° λ•Œλ¬Έμ— νƒ€μž…μ—λŸ¬κ°€ 났닀. νƒ€μž…μŠ€ν¬λ¦½νŠΈμ—μ„œ 큰 집합은 μž‘μ€ 집합에 할당될 수 μžˆμ§€λ§Œ, κ·Έλ ‡λ‹€κ³  큰집합이 μž‘μ€ 집합이라고 ν•  μˆ˜λŠ” μ—†λ‹€.

μ‚¬μžλŠ” 동물에 ν¬ν•¨λœλ‹€ λΌλŠ”λ§μ€ μ‚¬μžκ°€ 동물에 ν¬ν•¨λ˜κΈ° λ•Œλ¬Έμ— λ§žμ§€λ§Œ, μ‚¬μžμ™€ 동물은 κ°™λ‹€λΌλŠ” λ§μ—λŠ” 였λ₯˜κ°€ μžˆλŠ” 것과 κ°™λ‹€.

Guidelines for Writing Good Generic Functions

Generic ν•¨μˆ˜λ₯Ό 잘 μž‘μ„±ν•˜λŠ” 방법 3가지λ₯Ό μ •λ¦¬ν•΄λ³΄μž.

  • Push Type Param Down: param을 더 ꡬ체적인 κ°’μœΌλ‘œ μ‚¬μš©ν•œλ‹€.
function firstElement1<Type>(arr: Type[]) {
  return arr[0];
}
 
function firstElement2<Type extends any[]>(arr: Type) {
  return arr[0];
}
 
// a: number (good)
const a = firstElement1([1, 2, 3]);
// b: any (bad)
const b = firstElement2([1, 2, 3]);
  • Use Fewer Type Parameters: 더 적은 νƒ€μž…μ˜ param으둜 μ΄μš©ν•œλ‹€.
function filter1<Type>(arr: Type[], func: (arg: Type) => boolean): Type[] {
  return arr.filter(func);
}
 
function filter2<Type, Func extends (arg: Type) => boolean>(
  arr: Type[],
  func: Func
): Type[] {
  return arr.filter(func);
}

filter2의 κ²½μš°λŠ” μ–΄λ–€ ν•¨μˆ˜ νƒ€μž…μΈμ§€ 일일이 μ •ν•΄μ€˜μ•Όν•˜λ―€λ‘œ μ‚¬μš©μ²˜μ—μ„œ λΆˆνŽΈν•¨μ΄ μ‘΄μž¬ν•œλ‹€.

  • Type Parameters Should Appear Twice: μž¬μ‚¬μš©μ΄ ν•„μš”ν•œ μƒν™©μ—λ§Œ μ œλ„€λ¦­μ„ μ“°μž.
function greet<Str extends string>(s: Str) {
  console.log("Hello, " + s);
}
 
greet("world");


function greet(s: string) {
  console.log("Hello, " + s);
}

훨씬 κ°„λ‹¨ν•˜κ²Œ μ‚¬μš©ν•  μˆ˜μžˆλŠ” 방법을 고민해보고, λ°˜λ³΅λ˜λŠ” νƒ€μž…μ— ν•œν•΄μ„œ μž¬μ‚¬μš©μ„ μœ„ν•΄ μ œλ„€λ¦­μ„ μ“°μž.

Optional Parameters

param이 없을 수 μžˆλŠ” 경우λ₯Ό μœ„ν•΄ ?λ₯Ό μ΄μš©ν•  수 μžˆλ‹€. μ΄λ ‡κ²Œ μ‚¬μš©ν•˜κ²Œ 되면 T|undefined 둜 νƒ€μž…μ΄ ν• λ‹Ήλœλ‹€.

function f(x?: number) {
  // ...
}
f(); // OK
f(10); // OK

λ˜λŠ” param이 없을 λ•Œλ₯Ό μœ„ν•œ κΈ°λ³Έ 값을 parameter default둜 μ •μ˜ν•  수 μžˆλ‹€.

function f(x = 10) {
  // ...
}

μ£Όμ˜ν•  점은 callback을 μ΄μš©ν•  λ•Œμ˜ μ˜λ―ΈλŠ” optionalν•œ parameterλŠ” ν•„μš”μ—†μ΄ 호좜 될 수 μžˆμŒμ„ μ˜λ―Έν•˜λŠ” κ²ƒμ΄λ‹ˆκΉŒ λΆˆν•„μš”ν•˜κ²Œ μ‚¬μš©ν•˜μ§€ 말자.

callback
callback

Function Overloads

ν•¨μˆ˜ μ˜€λ²„λ‘œλ“œλŠ” ν‰μ†Œμ— 잘 μ‚¬μš©ν•˜μ§€ μ•Šλ˜ 뢀뢄이닀 λ³΄λ‹ˆ μ΄ν•΄ν•˜λŠ”λ° 어렀움이 μžˆμ—ˆλ‹€. ν•¨μˆ˜ OverloadλŠ” 같은 μ΄λ¦„μ˜ ν•¨μˆ˜μ— param이 λ‹€λ₯΄κ²Œ μ •μ˜ν•˜λŠ” 방법이닀.

function makeDate(timestamp: number): Date;
function makeDate(m: number, d: number, y: number): Date;
function makeDate(mOrTimestamp: number, d?: number, y?: number): Date {
  if (d !== undefined && y !== undefined) {
    return new Date(y, mOrTimestamp, d);
  } else {
    return new Date(mOrTimestamp);
  }
}
const d1 = makeDate(12345678);
const d2 = makeDate(5, 5, 5);

Overload ν•¨μˆ˜λ₯Ό μž‘μ„±ν•˜λŠ”λ°μ—λŠ” λͺ‡κ°€μ§€ κ·œμΉ™μ΄ μžˆλ‹€.

  • μΈμžκ°€ 없을 λ•Œμ˜ μ–΄λ–€ ν•¨μˆ˜μΈμ§€ κ΅¬ν˜„μ΄ ν•„μš”ν•˜λ‹€.

overload1
overload1

μœ„ μ˜ˆμ œμ—μ„œ μ—λŸ¬κ°€ λ°œμƒν•œ μ΄μœ λŠ” param이 없을 λ•Œμ˜ ν•¨μˆ˜κ°€ μ–΄λ–»κ²Œ κ΅¬ν˜„λ μ§€ μ •μ˜λ˜μ§€ μ•Šμ•˜κΈ° λ•Œλ¬Έμ΄λ‹€.

μ•„λž˜μ™€ 고치면 νƒ€μž…μ—λŸ¬κ°€ μ‚¬λΌμ§€λŠ” 것을 λ³Ό 수 μžˆλ‹€.

function fn(x: string): void;
function fn():void;
function fn() {
  // ...
}
// Expected to be able to call with zero arguments
fn();
  • Overload 끼리 compatibleν•΄μ•Όν•œλ‹€.

overload2
overload2

μœ„ μ˜ˆμ œμ—μ„œ 같은 param 갯수λ₯Ό κ°€μ§€λŠ” overload ν•¨μˆ˜λ₯Ό μž‘μ„±ν–ˆλ‹€. νƒ€μž…μ—λŸ¬κ°€ λ°œμƒν•œ μ΄μœ λŠ” λ¨Όμ € μž‘μ„±ν•œ overload ν•¨μˆ˜ param νƒ€μž…μ΄ boolean으둜 μ •μ˜λœ μƒν™©μ—μ„œ 같은 param 갯수λ₯Ό κ°€μ§€λŠ” λ‘λ²ˆμ§Έ overload ν•¨μˆ˜μ—μ„œ booleanνƒ€μž…κ³Ό compatible ν•˜μ§€ μ•Šμ€ string νƒ€μž…μœΌλ‘œ μ •μ˜ν–ˆκΈ° λ•Œλ¬Έμ΄λ‹€.

  • Overload ν•¨μˆ˜λŠ” μ‘°κ±΄λΆ€λ‘œ 쓰이면 μ•ˆλœλ‹€.
function len(s: string): number;
function len(arr: any[]): number;
function len(x: any) {
  return x.length;
}

μœ„ μ˜ˆμ œλŠ” stringκ³Ό arrayλ₯Ό param으둜 받을 수 μžˆλŠ” ν•¨μˆ˜ overloadκ°€ μ •μ˜λœ 상황이닀.

overload3
overload3

μœ„ νƒ€μž…μ—λŸ¬λŠ” 쑰건에 따라 λ‹€λ₯Έ νƒ€μž…μ˜ param을 μ „λ‹¬ν•˜λŠ” 상황이기 λ•Œλ¬Έμ— λ°œμƒν–ˆλŠ”λ°, κ·Έ μ΄μœ λŠ” νƒ€μž…μŠ€ν¬λ¦½νŠΈλŠ” ν•˜λ‚˜μ˜ overLoadμ—μ„œλ§Œ ν•¨μˆ˜λ₯Ό ν˜ΈμΆœν•˜κΈ° λ•Œλ¬Έμ΄λ‹€. μ΄λŸ΄λ•ŒλŠ” param을 μ •μ˜ν•  λ•Œ union type으둜 μ •μ˜ν•˜λŠ” 게 더 μ˜¬λ°”λ₯Έ 방법이닀.

function len(x: any[] | string) {
  return x.length;
}

this 닀루기

javascriptμ—μ„œμ˜ this 처럼 typescript의 this도 λ™μ μœΌλ‘œ μ •μ˜λœλ‹€.

const user = {
  id: 123,
  admin: false,
  becomeAdmin: function () {
    this.admin = true;
  },
};

μœ„ μ˜ˆμ œμ—μ„œ thisλŠ” μ•”μ‹œμ  바인딩에 μ˜ν•΄ ν˜ΈμΆœλ˜λŠ” μœ„μΉ˜μ— 따라 λ‹€λ₯Έ 값을 μ˜λ―Έν•˜κ²Œλœλ‹€.

μ΄λŸ¬ν•œ thisλ₯Ό μƒλŒ€μ μœΌλ‘œ μ‰½κ²Œ(?) μ •μ˜ν•˜λŠ” λ°©λ²•μœΌλ‘œ ν™”μ‚΄ν‘œν•¨μˆ˜λ₯Ό μ΄μš©ν•˜λŠ” 방법이 μžˆλ‹€. ν™”μ‚΄ν‘œ ν•¨μˆ˜λŠ” ν•¨μˆ˜ 선언식과 λ‹€λ₯Έ νŠΉμ§•μ΄ μžˆλŠ”λ° 그쀑 μƒμ„±μž ν•¨μˆ˜λ‘œ 쓰일 수 μ—†λ‹€λŠ” 점이 μžˆλ‹€. κ·Έλ ‡κΈ° λ•Œλ¬Έμ— 자체적인 thisλ₯Ό 가지지 μ•Šκ³  ν™”μ‚΄ν‘œ ν•¨μˆ˜μ—μ„œ thisλŠ” μƒμœ„ μŠ€μ½”ν”„μ˜ thisλ₯Ό μ°Έμ‘°ν•˜λŠ” κ·œμΉ™ 가진닀.

this
this
μœ„ μ½”λ“œμ—μ„œ νƒ€μž… μ—λŸ¬κ°€ λ°œμƒν•œ μ΄μœ λŠ” ν™”μ‚΄ν‘œν•¨μˆ˜λ‘œ μ •μ˜λœ λ©”μ†Œλ“œμ˜ thisλŠ” μƒμœ„ μŠ€μ½”ν”„μΈ globalThis이기 λ•Œλ¬Έμ΄λ‹€.

Other Types to Know about

λͺ‡κ°€μ§€ μ•žμ„œ μ†Œκ°œλ˜μ§€ μ•Šμ•˜λ˜ νƒ€μž…λ“€μ— λŒ€ν•΄ μ •λ¦¬ν•΄λ³΄μž.

  • Void

ν•¨μˆ˜μ—μ„œ λ°˜ν™˜ν•˜λŠ” 값이 없을 λ•Œλ₯Ό μœ„ν•œ νƒ€μž…μœΌλ‘œ, return 문이 없을 λ•Œ μžλ™μœΌλ‘œ undefined이 λ°˜ν™˜λ˜μ§€λ§Œ νƒ€μž…μŠ€ν¬λ¦½νŠΈμ—μ„œλŠ” λ”°λ‘œ void둜 μ •μ˜ν•œλ‹€.

  • unknown

any와 μœ μ‚¬ν•˜μ§€λ§Œ, any와 달리 μ–΄λ–€ 속성을 κ°€μ§€λŠ” 지 μ•Œ 수 μ—†κΈ° λ•Œλ¬Έμ— 속성에 μ ‘κ·Όν•  수 μ—†λ‹€. 주둜 try-catch 문으둜 μ—λŸ¬λ₯Ό 받을 λ•Œ unknown으둜 λ°˜ν™˜λ˜κΈ° λ•Œλ¬Έμ— λΉ„μ¦ˆλ‹ˆμŠ€ μ—λŸ¬λ‘œ μ μ ˆν•˜κ²Œ νƒ€μž…μ„ μ •μ˜ν•œ ν›„ instanceOf λ₯Ό μ΄μš©ν•΄ μ—λŸ¬ 객체λ₯Ό μ •μ˜ν•˜λŠ” λ°©μ‹μœΌλ‘œ ν˜„μ—…μ—μ„œ μ‚¬μš©ν•˜κ³  μžˆλ‹€.

  • never

neverλŠ” μ–΄λ–€ 것도 포함될 수 μ—†λŠ” νƒ€μž…μ„ μ˜λ―Έν•œλ‹€. unionμ΄λ‚˜ switchλ¬Έμ—μ„œ 더이상 μ—†λ‹€λŠ” 것을 μ˜λ―Έν•œλ‹€.

function fn(x: string | number) {
  if (typeof x === "string") {
    // do something
  } else if (typeof x === "number") {
    // do something else
  } else {
    x; // has type 'never'!
  }
}

Rest Parameters and Arguments

Rest ParameterλŠ” μΈμžκ°€ λ™μ μœΌλ‘œ λ“€μ–΄μ˜¬ λ•Œμ˜ param을 μ˜λ―Έν•œλ‹€. arguments둜 받을 param의 νƒ€μž…λ“€μ„ μ •μ˜ν•  수 μžˆλ‹€.

function multiply(n: number, ...m: number[]) {
  return m.map((x) => n * x);
}
// 'a' gets value [10, 20, 30, 40]
const a = multiply(10, 1, 2, 3, 4);

rest parameterλ₯Ό 생길 수 μžˆλŠ” νƒ€μž…μ—λŸ¬λ‘œλŠ” λ°°μ—΄κ³Ό νŠœν”Œμ˜ νƒ€μž… μ°¨μ΄μ—μ„œ λ°œμƒν•  수 μžˆλ‹€.

rest
rest

μœ„ μ˜ˆμ œμ—μ„œ Math.atan2(...)λ©”μ†Œλ“œλŠ” λ”± λ‘κ°œμ˜ 인자λ₯Ό λ°›μ§€λ§Œ argsλŠ” number[]둜 νƒ€μž…μ΄ λ˜μ–΄μžˆκΈ° λ•Œλ¬Έμ— λͺ‡κ°œμ˜ 값이 더 λ“€μ–΄μ˜¬μ§€ λͺ¨λ₯΄λŠ” νƒ€μž…μ„ μ˜λ―Έν•΄ 생긴 νƒ€μž… μ—λŸ¬λ‹€.

이λ₯Ό ν•΄κ²°ν•˜κΈ° μœ„ν•΄μ„œλŠ” 배열이 μ•„λ‹ˆλΌ λͺ‡κ°œμ˜ μš”μ†Œλ‘œ λ˜μ–΄μžˆλŠ” νƒ€μž…μΈμ§€ μ •μ˜λœ tuple을 μ΄μš©ν•˜λ©΄ ν•΄κ²°ν•  수 μžˆλ‹€.

const args = [8, 5] as const;
// OK
const angle = Math.atan2(...args);

Parameter Destructuring

객체 param을 전달할 λ•Œ 각 속성에 λŒ€ν•œ νƒ€μž…μ„ μ •μ˜ν•΄μ„œ 전달할 수 μžˆλ‹€.

type ABC = { a: number; b: number; c: number };
function sum({ a, b, c }: ABC) {
  console.log(a + b + c);
}

Assignability of Functions

voidλ₯Ό λ°˜ν™˜νƒ€μž…μœΌλ‘œ κ°€μ§€λŠ” ν•¨μˆ˜λ“€ 끼리 κ°€μ§€λŠ” νŠΉμ΄ν•œ νŠΉμ§•μ΄ μžˆλ‹€. void μžμ²΄λŠ” ν•΄λ‹Ή ν•¨μˆ˜λ“€μ΄ λ°˜ν™˜ν•˜λŠ” νƒ€μž…μ— λŒ€ν•΄ κ°•ν•˜κ²Œ νƒ€μž…μ„ 따지지 μ•ŠκΈ° λ•Œλ¬Έμ— λ°˜ν™˜ νƒ€μž…μ΄ λ¬΄μ‹œλœλ‹€.

type voidFunc = () => void;
 
const f1: voidFunc = () => {
  return true;
};
 
const f2: voidFunc = () => true;
 
const f3: voidFunc = function () {
  return true;
};

μœ„ μ˜ˆμ œμ—μ„œ f1,f2,f3 λ‹€ λ°˜ν™˜ν•˜λŠ” νƒ€μž…μ΄ booleanμ΄μ§€λ§Œ voidFunc둜 μ •μ˜ν•΄λ„ νƒ€μž…μ—λŸ¬κ°€ λ°œμƒν•˜μ§€ μ•ŠλŠ”λ‹€.

function f2(): void {
  return true; // μ—λŸ¬ λ°œμƒ
}

ν•˜μ§€λ§Œ 직접 μ •μ˜ν•  λ•Œ voidλ₯Ό λ°˜ν™˜ν•œλ‹€κ³  μ •μ˜ν•  μ‹œμ— νƒ€μž… μ—λŸ¬κ°€ 걸리게 λœλ‹€.

@Troy
맀일의 μ‹œν–‰μ°©μ˜€λ₯Ό κΈ°λ‘ν•˜λŠ” κ°œλ°œμΌμ§€μž…λ‹ˆλ‹€.