타입 호환(type compatibility)은 서로 다른 타입이 2개 있을 경우 특정 타입이 다른 타입에 포함되는지를 의미합니다.
아래 코드에서 숫타입에 문자열 타입을 할당하려 하고 있습니다. 따라서 타입에러가 발생할 수 있습니다.
let a: string = "text";
let b: number = 10;
b = a;
위 코드와 달리 아래 코드에서는 string 타입에 "text" 라는 문자열 타입을 할당하였습니다. 이 경우에는 "text" 문자열이 string 타입에 포함되기 때문에 타입 에러는 발생하지 않습니다.
let a: string = "text";
let b: "text" = "text";
a = b;
즉, 아래와 같이 string 타입에 "text" 문자열이 포함되어 있기 때문에 타입 호환이 되는것입니다.
타입스크립트의 타입호환은 다른 언어와의 차이점이 존재합니다.
아래 코드에서는 name 속성을 갖는 인터페이스와 클래스를 선언하여 a 변수를 선언하고 인터페이스 타입 Person를 지정한 후 Devloper 클래스를 생성하여 변수에 할당한 코드입니다.
이렇게 하면 에러가 발생하지 않습니다. 이런 코드에서 에러가 발생하지 않는 이유는 구조적 타이핑(structural typing) 특성 때문입니다.
interface Person {
name: "string";
}
class Developer {
name: string;
}
let a: Person;
a = new Developer();
구조적 타이핑(structural typing)이란 타입 유형보다는 타입 구조로 호환 여부를 판별하는 언어적 특성을 의미합니다.
아래 코드에서는 분명 타입 별칭으로 선언된 Person 타입과 인터페이스로 선언된 Developer 타입은 서로 다른 타입으로 타입 호환이 이루어질것 같지 않습니다. 하지만 b 변수(Developer)에 a 변수(Person)를 할당하면 에러가 발생하지 않고 할당이 이루어집니다. 이는 타입스크립트가 해당 타입이 어떤 구조 타입 구조를 갖는지를 타입 호환 여부로 판별하기 때문입니다.
여기서는 타입 별칭과 인터페이스에는 모두 name이라는 속성이 포함되기 때문에 타입 구조가 같다고 볼 수 있습니다.
타입 호환 여부를 판별할 때는 단순히 문자열 타입의 특정 속성 유무뿐 만 아니라 속성 이름도 일치하는지 확인합니다.
type Person = {
name: string;
}
interface Developer {
name: string;
}
let a: Person = {
name: "person"
}
let b: Developer = {
name: "developer"
}
b = a;
객체의 타입호환에 대해 자세히 살펴보겠습니다.
앞서 나왔듯 구조적 타이핑을 통해 객체 타입은 타입 유형에 관계없이 동일한 이름의 속석을 갖고 있고 해당 속성의 타입이 같으면 호환이 가능합니다.
아래 코드에서는 Designer 타입 별칭으로 선언한 변수 a에 Developer 인터페이스로 선언한 변수 b를 할당 시 에러가 발생하지 않지만 그 반대시 타입 에러가 발생합니다. Designer 타입 별칭의 속성을 Developer에서는 모두 만족하지만 Developer 인터페이스의 skill 속성이 Designer 타입 별칭에서는 없기 때문입니다.
type Designer = {
name: string;
}
interface Developer {
name: string;
skill: string;
}
let a: Designer = {
name: "designer"
skill: "design"
}
let b: Developer = {
name: "developer",
skill: "coding"
}
// 타입 에러 발생 x
a = b ;
// 타입 에러 발생 o : Designer 타입에 skill 속성이 없어 호환 x
b = a;
타입 에러를 해결하기 위해서는 Designer 타입 별칭 속성에 skill 속성을 추가하거나 Developer 인터페이스에서 skill 속성을 옵서녈 속성(?)로 변경하면됩니다.
type Designer = {
name: string;
skill: string;
}
interface Developer {
name: string;
skill: string;
}
let a: Designer = {
name: "designer"
skill: "design"
}
let b: Developer = {
name: "developer",
skill: "coding"
}
a = b ;
b = a;
type Designer = {
name: string;
}
interface Developer {
name: string;
skill: string;
}
let a: Designer = {
name: "designer"
}
let b: Developer = {
name: "developer",
skill?: "coding"
}
a = b ;
b = a;
let getNumber = (num: number) => {
return num;
}
let sum = (a: number, b: number) => {
return a + b;
}
sum = getNumber;
// sum() 함수가 아래와 같이 변경됨
// sum = (num) => {
// return num;
// }
console.log(sum(10, 20)) // 10;
// 타입 에러 : 파라미터가 일치하지 않음
getNumber = sum;
// getNumber() 함수가 아래와 같이 변경됨
// getNumber = (a, b) => {
// return a + b;
// }
// 인수 1개를 넘기기 때문에 10 + undefined가 반환
console.log(getNumber(10)); // NaN
위 코드에서 getNumber, sum 이라는 함수가 함수 표현식으로 정의되어있습니다.
sum() 함수를 getNumber() 함수에 할당시에는 타입 에러가 발생하지 않지만 getNumber() 함수를 sum()함수에 할당할시 타입 에러가 발생합니다.
함수의 파라미터가 2개인 sum()함수에 파라미터가 1개인 getNumber() 함수를 할당하면 함수의 역할이 달라지기 때문입니다.
함수의 타입 호환은 기존 함수 코드의 동작을 보장하는 것으로 이해할 수 있습니다. 아래 그림과 같이 특정 함수 타입의 부분 집합에 해당하는 함수는 호환되지만 더 크거나 타입을 만족하지 못하는 함수는 호환되지 않습니다.
이넘 타입의 경우 숫자형 이넘은 숫자 타입과 호환이 되지만 이넘 타입간의 호환은 불가능합니다.
즉, 이넘 타입은 같은 속성과 값을 가졌더라도 이넘 타입 간에는 서로 호환되지 않습니다.
enum myEnum {
A, // 0
B, // 1
C // 2
}
let a: number = 10;
// 타입 호환 o
a = myEnum.A;
enum myEnum2 {
A, // 0
B, // 1
C // 2
}
// 타입 호환 x
let b: myEnum.A;
b = myEnum2.A;
제네릭의 타입 호환은 제네릭으로 받은 타입이 해당 타입 구조에서 사용되었는지에 따라 결정됩니다.
아래 첫 번째 코드에서는 제네릭에서 사용되는 타입이 다르지만 타입호환이 됩니다. 이것은 제네릭으로 받은 타입이 해당 타입 구조에서 사용되지 않는다면 타입 호환에 영향을 미치지 않기 때문입니다.
두 번째 코드에서는 제네릭으로 받은 타입이 다르기 때문에 타입 호환이 되지않아 타입 에러가 발생합니다.
interface myInterface<T> {
}
let a: myInterface<string> = "";
let b: myInterface<number> = 10;
// 타입 호환 O : 제네릭으로 받은 타입이 사용되지 않음
a = b ;
b = a;
interface myInterface<T> {
value: T;
}
let a: myInterface<string> = {value: ""};
let b: myInterface<number> = {value: 10};
// 타입 호환 x : 타입 구조가 다르기 때문
a = b ;
b = a;
- 타입 호환(type compatibility)은 서로 다른 타입이 2개 있을 경우 특정 타입이 다른 타입에 포함되는지를 의미합니다.
- 타입스크립트의 타입 호환은 구조적 타이핑(structural typing) 이라는 언어적 특성으로 다른 언어와 차이점을 보입니다.
- 구조적 타이핑(structural typing)이란 타입 유형보다는 타입 구조로 호환 여부를 판별하는 언어적 특성을 의미합니다.
- 객체 타입의 호환은 타입 유형에 관계없이 동일한 이름의 속석을 갖고 있고 해당 속성의 타입이 같으면 호환이 가능합니다.
- 함수 타입의 호환은 기존 함수 코드의 동작을 보장하는 것으로 결정됩니다.
- 이넘 타입의 호환은 이넘 타입의 경우 숫자형 이넘은 숫자 타입과 호환이 되지만 이넘 타입간의 호환은 불가능합니다.
- 제네릭 타입의 호환은 제네릭의 타입 호환은 제네릭으로 받은 타입이 해당 타입 구조에서 사용되었는지에 따라 결정됩니다.