Typescript: 함수를 과도하게 입력하고 있습니까?

Typescript에서 함수를 정의할 때 첫 번째 본능은 기존 인터페이스 또는 클래스를 사용하라고 말할 수 있습니다. 이것은 최적이 아닐 수 있습니다. 간단한 예부터 시작하겠습니다.

interface Cat {
  name: string;
  furColor: string;
  age: number;
}

const getName = (cat: Cat): string => {
  return cat.name;
}

const cat: Cat = { name: 'Garfield', furColor: 'orange', age: 3 }

console.log(getName(cat));


이 예에서 함수getName()는 고양이 이름(Garfield)을 반환합니다. 여태까지는 그런대로 잘됐다. 이제 새 인터페이스Person를 도입한다고 가정해 보겠습니다.

interface Person {
  name: string;
  age: number;
}

const person: Person = { name: 'John', age: 30 }

console.log(getName(person));


이제 typescript는 person 유형에 대해 불평합니다.

Argument of type 'Person' is not assignable to parameter of type 'Cat'.
  Property 'furColor' is missing in type 'Person' but required in type 'Cat'.


이것은 사람이 분명히 고양이가 아니기 때문에 이치에 맞습니다(대부분의 경우). 그러나 이것은 실제로 수신하는 개체의 이름만 사용하는 함수에 대해 너무 제한적입니다.

기능을 더 유연하게 만들기



해결책은 함수를 다시 작성하여 보다 유연하게 만드는 것입니다.

const getName = (obj: { name: string }): string => {
  return obj.name;
}


이제 고양이와 사람, 그리고 name 속성을 가진 다른 모든 개체에 사용할 수 있습니다. 이 기능은 다른 작업을 필요로 하지 않으므로 인위적으로 제한해서는 안 됩니다.

그런데 이것이 왜 효과가 있을까요? Typescript는 인수가 최소한 인터페이스에 정의된 속성을 가지고 있는지 확인하기 때문에 작동합니다. 실제 유형을 비교하지 않고 인수의 추가 속성을 무시합니다. 이것은 또한 내 요점을 설명하기 위해 이 방법으로 예제를 구성해야 한다는 것을 의미합니다. 매개변수의 유형으로 Person 대신 Cat를 사용했다면 컴파일러는 고양이도 허용했을 것입니다. 그러나 여전히 이 함수는 실제로 name 속성만 사용하고 있는데 왜 매개변수에 연령이 포함될 것으로 예상해야 합니까?

이 경우에는 이것이 명백해 보일 수 있습니다. 그러나 함수의 모든 속성을 사용하지 않고도 매개 변수의 유형이 상당히 제한적인 더 복잡한 함수를 많이 보았습니다.

한 걸음 더



generics 을 사용하면 더 잘할 수 있습니다.

const getName = <T extends { name: unknown }>(obj: T): T['name']  => {
  return obj.name;
}


이렇게 하면 name 의 유형을 알 필요조차 없습니다. 개체 유형에서 유추됩니다. 물론 이 특별한 경우에 이름이 문자열이 아닌 다른 것이 되는 것은 별 의미가 없습니다. 그러나 제네릭의 힘을 강조하는 데 도움이 됩니다.

결론



이것이 내가 이 기사에서 보여주고 싶었던 전부입니다. 함수의 매개변수에 기존 인터페이스를 그냥 두지 마십시오. 함수에 실제로 필요한 것과 이것만 필요한 것이 무엇인지 생각해 보십시오. 함수를 더 유연하고 쉽게 재사용할 수 있습니다.

제네릭은 코드를 더욱 재사용할 수 있도록 도와주는 강력한 도구입니다. 그것들은 확실히 더 탐구할 가치가 있습니다. 그러나 이것은 다른 시간에 주제가 될 것입니다.


이미지: 사진: 傅甬 华 on Unsplash

좋은 웹페이지 즐겨찾기