TypeScript - Function

이소라·2022년 7월 23일
0

TypeScript

목록 보기
9/28

Function

Function Type Expressions

  • function type expression
    • 함수를 묘사하는 가장 간단한 방법
    • arrow function과 문법적으로 유사함
function greeter(fn: (a: string) => void) {
  fn("Hello, World");
}
  • (a: string) => void : 문자열 타입인 a 매개변수를 가지고 있고, 반환값이 없는 함수 타입

  • function type expression에서 매개변수 이름이 꼭 들어가야 함

    • 예를 들어 (string) => void로 작성할 경우, any 타입인 string 매개변수를 가지고 있고 반환타입이 없는 타입이 됨
  • type alias를 사용하여 함수 타입에 이름을 지어줄 수 있음

type GreetFunction = (a: string) => void;
function greeter(fn: GreetFunction) {
  // ...
}



Call Signatures

  • JavaScript에서는 함수가 호출될 수 있는 속성값을 가질 수 있지만, function type expression에서는 속성값을 정의할 수 없음
  • 함수에서 속성값을 정의하기 위해서 객체 타입에서 call signature를 사용함
type DescribableFunction = {
  description: string;
  (someArg: number): boolean;
};
function doSomething(fn: DescribableFunction) {
  console.log(fn.description + " returned " + fn(6));
}
function fn(n: number) {
  return n + 1 > 6;
}
fn.description = 'black';

doSomething(fn); // 'black returned true'
  • call signature의 function type expression과의 차이점
    • 매개변수 목록과 반환 타입 사이에 ()=> 대신 :를 사용함
    • function의 속성을 정의할 수 있음
    • function overload를 할 수 있음
type Function = {
  (x: number, y: number): number;
  (x: number): boolean;
};



Construct Signature

  • construct signature는 call signature 앞에 new 키워드를 붙여서 작성함
type SomeConstructor = {
  new (s: string): SomeObject;
};
function fn(ctor: SomeConstructor) {
  return new ctor("hello");
}
  • Date와 같은 객체는 new 키워드와 같이 또는 없이 호출될 수 있음
    • 이러한 경우 같은 타입에 대해서 call signature와 construct signature를 둘 다 가질 수 있음
interface CallOrConstruct {
  new (s: string): Date;
  (n?: number): number;
}
  • class에 interface를 implement했을 때, class의 인스턴스의 타입만 확인되고 class의 static side인 construct의 타입은 확인되지 않음
  • class의 static side도 타입 확인을 되기 위해서, construct에 대한 타입 construct signature와 인스턴스에 대한 타입 둘 다 정의해야 함
interface ClockConstructor {
  new (hour: number, minute: number): ClockInterface;
}

interface ClockInterface {
  tick(): void;
}

function createClock(
  ctor: ClockConstructor,
  hour: number,
  minute: number
): ClockInterface {
  return new ctor(hour, minute);
}

class DigitalClock implements ClockInterface {
  constructor(h: number, m: number) {}
  tick() {
    console.log("beep beep");
  }
}

class AnalogClock implements ClockInterface {
  constructor(h: number, m: number) {}
  tick() {
    console.log("tick tock");
  }
}

let digital = createClock(DigitalClock, 12, 17);
let analog = createClock(AnalogClock, 7, 32);

construct signature - stackoverflow



Generic Function

  • 두 값 사이의 대응을 묘사하길 원할 때 generic을 사용함
  • function signature 안에 타입 매개변수 Type을 선언하여 작성됨
  • 타입 매개변수 Type를 추가함으로써 함수의 매개변수와 반환값 사이의 연결을 만듬
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([]);

Generic Function - Inference

  • 타입 매개변수 Type은 추론되므로 구체화하지 않아도 됨
  • 여러 타입 매개변수 Type을 사용해도 됨
function map<Input, Output>(arr: Input[], func: (arg: Input) => Output): Output[] {
  return arr.map(func);
}
 
// Parameter 'n' is of type 'string'
// 'parsed' is of type 'number[]'
const parsed = map(["1", "2", "3"], (n) => parseInt(n));

Generic Function - constraints

  • constraint를 사용하여 타입 매개변수로 받아들일 수 있는 타입 종류를 제한할 수 있음
  • constraintextends 절로 작성됨
function longest<Type extends { length: number }>(a: Type, b: Type) {
  if (a.length >= b.length) {
    return a;
  } else {
    return b;
  }
}
 
// longerArray is of type 'number[]'
const longerArray = longest([1, 2], [1, 2, 3]);
// longerString is of type 'alice' | 'bob'
const longerString = longest("alice", "bob");
// Error! Numbers don't have a 'length' property
const notOK = longest(10, 100);
  • Type{ length: number }로 제한했기 때문에 .length 속성에 접근할 수 있었음
  • type constraint가 없다면, 타입 매개변수가 다른 타입일 수 있기 때문에 해당 속성에 접근할 수 없음

Generic Function - Common Error for Constraints

function minimumLength<Type extends { length: number }>(
  obj: Type,
  minimum: number
): Type {
  if (obj.length >= minimum) {
    return obj;
  } else {
    return { length: minimum }; // Error
  /*
  Type '{ length: number; }' is not assignable to type 'Type'.
  '{ length: number; }' is assignable to the constraint of type 'Type', 
  but 'Type' could be instantiated with a different subtype of constraint '{ length: number; }'.
  */
  }
}

// 'arr' gets value { length: 6 }
const arr = minimumLength([1, 2, 3], 6);
// and crashes here because arrays have a 'slice' method, 
// but not the returned object!
console.log(arr.slice(0));
  • 위 함수는 Type이나 constraint와 일치하는 값을 반환함
    • constraint와 일치하는 같은 값을 반환하는 것이 아니라 constraint와 일치하는 종류의 값을 반환하므로 다른 타입을 반환할 수 있기 때문에 에러가 발생함

Generic Function - Specifying Type Argument

  • 일반적으로 generic function에서 타입 매개변수는 추론되지만, 의도적으로 타입을 선언할 수도 있음
function combine<Type>(arr1: Type[], arr2: Type[]): Type[] {
  return arr1.concat(arr2);
}

const arr = combine([1, 2, 3], ["hello"]); // Error : Type 'string' is not assignable to type 'number'.

const arr = combine<string | number>([1, 2, 3], ["hello"]); // OK

Generic Function - Writing Good Generic Functions

  1. constraint을 사용하기보다는 타입 매개변수 그 자체를 사용하기
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]);
  • firstElement1의 반환 타입은 Type 인 반면에 firstElement2의 반환 타입은 constraint에 의해 any
  1. 가능하면 적은 타입 매개변수를 사용하기
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에서 Func는 함수에 들어오는 타입 매개변수와 반환값과 관련이 없으므로 타입 매개변수로 선언하지 않아도 됨
  1. 타입 매개변수는 여러 값의 타입이 연관 있을 때만 사용하기
// bad
function greet<Str extends string>(s: Str) {
  console.log("Hello, " + s);
}

// good
function greet(s: string) {
  console.log("Hello, " + s);
}
  • function signature에서 타입 매개변수가 한번만 사용될 경우, 타입 매개변수를 사용하지 않아도 됨



Optional Parameters

  • ? 키워드를 붙인 매개변수는 선택적으로 받을 수 있음
function f(x?: number) {
  // ...
}
f(); // OK
f(10); // OK
  • optional paramter의 값이 전달되지 않은 경우, 그 매개변수에 undefined가 전달됨

Optional paramters in Callbacks

  • callback 함수에는 절대 optional paramter를 사용하지 않기
    • 예외사항 : optional parameter를 전달하지 않고 callback 함수를 호출하는 경우
function myForEach(arr: any[], callback: (arg: any, index?: number) => void) {
  for (let i = 0; i < arr.length; i++) {
    callback(arr[i]);
  }
}

myForEach([1, 2, 3], (a, i) => {
  console.log(i.toFixed()); // Error : Object is possibly 'undefined'.
});

0개의 댓글