인프런 커뮤니티 질문&답변

const.takeo님의 프로필 이미지
const.takeo

작성한 질문수

타입스크립트 입문 - 기초부터 실전까지

타입 호환 예제 - 함수, 제네릭

인터페이스와 함수의 차이

작성

·

794

1

인터페이스에서는 프로퍼티가 많은걸 구조적으로 크다고 말씀하셨는데 인터페이스에서는 구조적으로 작은게 큰거에 호환이 안되었습니다. 그런데 함수는 반대로 파라미터가 많은 즉 구조적으로 넒은 함수가 구조적으로 작은 함수랑 호환이 되었습니다.

왜 그런건가요? 어떻게 이해해야 될지 모르겠습니다. 

답변 6

2

크다 작다로 접근하게 되서 크기를 재려고(?)하니 어려워지는게 아닌가 싶습니다.

단순하게

interface A {
   prop1: string
}

interface B {
   prop1: string;
   prop2: number;
}

let a: A = { prop1: `type A`}
let b: B = { prop1: `type B`, prop2: 2}

의 경우

a 는 prop1를 포함하는 어떤 객체든 가질 수 있고
b는 prop1, prop2를 포함하는 어떤 객체든 가질 수으니

a 에는 b를 할당할 수 있으나 (a는 prop1만 분명히 존재하면 되니까)

a = b; // possible

b 에는 a를 할당할 수 없고 (b는 prop2가 없으면 타입 오류인데 a는 prop1으로만 존재하는 경우도 있으니까)

b = a // type error

let fn1 = function(a: number) {}
let fn2 = function(a: number, b: number) {}

이런 경우라면

fn1는 number 타입의 파라미터 하나만 받는 함수를 가질 수 있고
fn2는 number 타입의 파라미터와 string 타입의 파라미터를 추가로 받는 함수를 가질 수 있으니

fn1에는 prop1 한개만 받으니까 prop1과 prop2 두 개 파라미터를 받아야 하는 fn2를 할당 할 수 없고

fn1 = fn2 // type error

fn2에는 prop1과 prop2를 받으니까 prop1과 undefined인 prop2를 전달하게 되는 셈인 fn1은 할당 가능한

fn2 = fn1 // possible

형태로 생각하면 되지 않을까 싶습니다.

(그런데 제가 제대로 이해한건 맞는..거.........겠............죠......?)

1

저의 정리방식 입니다.. 맞는지는 모르겠지만 저는 이렇게 외웠어요! 

  • 타입 호환 인터페이스, 클래스는 자신(왼쪽)이 상대(오른쪽)에 포함되는 지에 따라 가능 / 불가능. 즉, 오른쪽이 더 크면(속성 많으면) 가능.
  • 타입 호환 함수는 자신(왼쪽)의 필요조건(파라미터)이 상대(오른쪽)의 필요조건(파라미터)보다 많은지에 따라 가능 / 불가능. 즉, 내가 상대보다 필요조건이 많을 때(조건이 많아 더 까다로울 때) 나보다 조건이 적은 상대(덜 까다로운)에 속해질 수 있다.  

1

저도 헷갈리네요. 그냥 객체는 프로퍼티가 부족해도 문제가 안생기지만 함수는 파라미터가 부족할 경우 문제가 발생할 수도 있다고 이해했습니다.

0

저도 이 점이 많이 궁금해서 많이 생각해봤습니다ㅠㅠ

인터페이스 클래스를 보면서 타입 호환을 체크할때 할당하는 값(우항)의 타입이 할당받는 값(좌항)의 타입의 합집합 일때는 호환이 가능하다고 이해했습니다.

핸드북에서는 객체가 할당된 변수를 함수 파라미터에 인자로 넘기는데 이때도 객체의 구조가 파라미터 구조의 합집합이 되므로 호환이 가능합니다. 그런데 이 경우도 파라미터 a가 인터페이스로 정의한 타입을 가지므로 사실 인터페이스에서의 타입 호환과 같은 경우인 것 같습니다.

반면 강의 예제에서는 함수끼리 비교를 다루고 있는데 이 경우는 관점을 반대로 바꿔서 생각해야합니다. 파라미터 구조를 기준으로 sum은 add의 합집합이지만 호환이 불가능하고 반대의 경우는 호환이 되기 때문에  좌항의 값이 우항의 값의 합집합인지 부분집합인지 따져야할 것 같습니다. 

제가 생각할때는 객체 프로퍼티와 함수의 파라미터라는 차이에서 오는 것 같습니다. 서로 타입이 다른 객체를 호환할 때에는 할당받는 쪽의 타입의 프로퍼티가 모자라도 아무 상관이 없습니다. 인터페이스를 정의할때 대상 객체의 프로퍼티의 일부만 정의해도 에러가 나지 않는 것 처럼요. 하지만 만약 인터페이스에서 어떤 프로퍼티를 정의를 했는데 실제 객체에는 없다면 그건 문제가 될겁니다.

하지만 함수끼리의 경우 (타입이 동일하다고 하고 파라미터 구조만 따졌을 때) 할당받는 쪽의 파라미터가 할당되는 쪽보다 모자라다면 할당되는 함수의 파라미터 일부는 제대로 인자 값을 받을 수 없을 겁니다. 할당받는 쪽에서는 할당되는 쪽에서 필요한 파라미터가 아예 없을테니까요 .

반대로 할당받는 함수의 파라미터 구조가 할당되는 함수의 파라미터 구조를 포함할 수 있다면 할당되는 함수는 아무 문제가 없을 겁니다.

즉 함수끼리의 호환에서는 인터페이스-객체의 경우와는 달리 할당되는 함수가 할당받는 쪽에서 정의한 것보다 실제 파라미터가 모자란 것은 상관없지만, 넘치는 것은 문제가 된다고 볼 수 있을 것 같습니다. 그리고 함수끼리의 경우에는 파라미터 뿐만아니라 리턴 값의 타입까지 체크하게 되네요.

사실 그냥 보면 당연한 것이긴 한데 질문을 읽고 생각해보니 많이 헷갈려서 나름대로 정리 해봤습니다. 저는 이렇게 이해를 해봤는데 잘못된 점이 있으면 고쳐주시면 감사하겠습니다ㅠㅠ

 

0

저도 비슷한 질문인것 같은데요,!
인터페이스 클래스 시간에는
오른쪽이 구조적으로 더 커야,
함수 제네릭 시간에는
왼쪽이 구조적으로 더 커야,
타입호환이 가능하다고 하셔서 헷갈립니다 :)

0

안녕하세요 const님, 좋은 질문이네요 :) 인터페이스의 경우는 특정 집합을 만족하는 합 집합 개념으로 보시면 좋을 것 같구요. 함수의 경우는 어떤 케이스를 말씀하시는지 혹시 코드로 적어주실 수 있을까요?

const.takeo님의 프로필 이미지
const.takeo

작성한 질문수

질문하기