먼저, 타입스크립트가 다른 언어들과 다른점은?
.js
파일에 있는 코드는 이미 TS라고 할 수 있다!interface
를 이용해서 의도를 명시하기 interface State { //interface를 통해 "의도"를 명시
name: string;
capital: string;
}
const states3: State[] = [
{ name: "Alabma", capitol: "Montgomery" }, //error: 'State'형식에 'capitol'이(가) 없습니다. 'capital'을 쓰려고 했습니까?
{ name: "Alabma", capitol: "Montgomery" },
{ name: "Alabma", capitol: "Montgomery" },
];
for (const state of states3) {
console.log(state.capital);
}
noImplicitAny
:변수들에게 미리 정의된 타입을 주지 않으면 오류가 나도록 하는 설정strict NullChecks
: null과 undefined가 모든 타입에서 허용되는지 확인하는 설정const x: number = null; // 해제되었을 때 : 정상
const x: number = null; // 설정했을 때 : 'null' 형식은 'number'형식에 할당할 수 없습니다.
✅ 결론 : 웬만한 상황에서 두 가지 다 설정하자 !
tsconfig.json
에 noEmitOnError
를 설정실제로 JS로 컴파일되는 과정에서 모든 인터페이스, 타입, 타입 구문은 그냥 제거되어 버리기 때문
👉 그렇다면 런타임에 타입 정보를 유지하는 방법은?
if('height' in Shape) {
// ...
}
interface Square2 {
kind: "square"; //태그
width: number;
}
interface Rectangle {
kind: "rectangle"; //태그
width: number;
height: number;
}
type Shape2 = Square2 | Rectangle; //태그된 유니온
function calculateArea2(shape: Shape2) {
if (shape.kind === "rectangle") { // 태그를 이용하여 분기 처리
return shape.width * shape.height;
} else {
return shape.width * shape.width;
}
}
타입 연산도 마찬가지로 컴파일 시 제거되므로 값을 정제해야 하는 경우 JS 연산을 통해 변환을 수행해야 한다.
function asNumber(val: number | string) : number {
return val as number; //타입 연산
}
🔽🔽🔽 ✅ 이런 코드를 지향하자!
function asNumber(val: number | string) : number {
return typeof(val) === 'string' ? Number(val) : val;
런타임에 타입이 모두 제거되는 것을 고려했을 때 함수 오버로딩은 불가능하다. 온전히 타입 수준에서만 여러 개의 선언문 작성이 가능하지만, 구현체는 오직 하나 뿐
function add(a: number, b: number) { // error: 중복된 함수 구현입니다.
return a + b;
}
function add(a: string, b: string) { // error: 중복된 함수 구현입니다.
return a + b;
}
덕 타이핑이란?
객체가 어떤 타입에 부합하는 변수와 메서드를 가질 경우 객체를 해당 타입에 속하는 것으로 간주하는 방식
구조적 타이핑 예시
NamedVector
구조가 Vector2D
와 호환되기 때문에 caculateLength 호출이 가능하다! --> 타입이 열려있다.interface Vector2D {
x: number;
y: number;
}
function calculateLength(v: Vector2D) {
return Math.sqrt(v.x + v.y);
}
interface NamedVector {
name: string;
x: number;
y: number;
}
const v: NamedVector = { x: 3, y: 4, name: "Zee" };
calculateLength(v);
interface Vector3D {
x: number;
y: number;
z: number;
}
const vec3D = {x:3, y:4, z:1, address:'123 Broadway'}
function calculateLengthL1(v:Vector3D) {
let length = 0;
for (const axis of Object.keys(v)) {
const coord = v[axis]; // 구조적 타이핑에 의해 추가된 속성으로 위 vec3D처럼 axis에 string이 들어올 수도 있는 것..!
length += Math.abs(coord);
}
return length;
}
🔽🔽🔽 ✅ 결론 : 루프보다는 모든 속성을 각각 더하는 구현이 낫다.
function calculateLengthL1(v: Vector3D) {
return Math.abs(v.x) + Math.abs(v.y) + Math.abs(v.z);
}
마지막으로,
❗ any 타입은 타입 체커와 타입스크립트 언어 서비스를 무력화시켜버리므로 최대한 지양하자!
구조적 타이핑 신기하네요!! 그동안 타입스크립트는 타입을 엄밀하게 지정한다는 생각을 하고 있었는데, 저렇게 호환이 될 수도 있고, 이 경우 map 을 돌렸을 때 원하지 않는 결과가 나올 수도 있다는 사실이 너무 흥미로워요!! 잘 봤습니다 :)