이펙티브 타입스크립트 스터디 - 1주차 발표 정리
주제 : 1장 (타입스크립트 알아보기)
1. 타입스크립트 알아보기
아이템 1. 타입스크립트와 자바스크립트의 관계 이해하기
"타입스크립트는 타입이 정의된 자바스크립트의 상위집합이다" 라는 말을 들어봤을 것이다.
이 말을 좀 더 자세히 풀어보면 자바스크립트는 타입스크립트라는 명제는 맞지만, 타입스크립트는 자바스크립트라는 명제는 틀리다.
타입스크립트는 타입을 명시하는 추가적인 문법을 가지기 때문이다.
const word = 'hello'
console.log(word.toUppercase()) // c가 대문자 C여야 한다.
js 내의 파일에 위 코드를 작성하면 코드 작성시에 에러를 뱉지 않는다
하지만, 파일 확장자를 ts로 바꾸게 되면 이렇게 에러를 뱉어준다.
* 이것은 코드를 작성할때 디버깅하기 쉽게 도와줘서 정말 도움이 많이 된다.
실무에서 바로 TS를 사용하다보면, 이러한 이점이 당연하게 느껴질 수 있는데 이때문에 크게 생각없이 얻고 있었던 이점같다.
타입 시스템의 목표 중 하나는 런타임에 오류를 발생시킬 코드를 미리 찾아내는 것이다.
const state = [
{ name: 'Alabama', capital: 'Montgomery' }
];
console.log(state.capitol); // o => a
위처럼 스펠링의 오류가 나더라하더라도 JS에서는 바로 오류를 인지하지 못한다.
만약, 사용자가 저것이 오류라는 것을 인지하지못한 채 프로그램을 실행했다면 console.log에 undefined만 찍히는 것을 확인할 수 있고,
프로그램의 크기가 클 시에 오류의 원인을 찾는데 시간을 생각보다 많이 쏟을 수도 있다.
.
이러한 불편한 점을 타입스크립트를 사용함으로써 미연의 방지를 할 수 있기 때문에 코드의 동작과 의도를 쉽게 파악할 수 있다.
interface State {
name: string;
capital: string;
}
const state: State[] = [
{ name: 'Alabama', capitol: 'Montgomery' } // <= 에러
];
여기에 추가로 타입 구문도 추가하면 더 확실하게 타입 체크를 할 수 있다.
타입스크립트 타입 시스템은 자바스크립트의 런타임 동작을 '모델링'한다.
따라서, 런타임 오류를 발생시키는 코드들을 찾아내려고 한다.
그러나, 너무 타입스크립트를 믿으면 안된다. 타입 체커마저 못 찾는 오류가 존재할 수 있다.
const names = ["alice", "bob"];
console.log(names[2].toUpperCase());
위 코드는 타입 체커를 통과하지만, 런타임에 오류가 발생한다.
타입스크립트는 실제로 자바스크립트 코드를 실행해보면서까지 검사를 하지 않는다.
타입스크립트는 그저 코드의 구문과 타입을 검사하므로 실제로 코드가 실행되면서 발생하는 오류까지 방지할 수는 없다.
아이템 2. 타입스크립트 설정 이해하기
타입스크립트 설정 파일은 tsc --init만 실행하면 간단히 생성됨.
타입스크립트 설정을 제대로 하려면 다음 두가지 설정에 대해 이해해야한다.
1. noImplicitAny
- noImplicitAny 옵션은 TypeScript에서 암시적인 any 타입의 사용을 금지하는 옵션이다.
- TypeScript에서는 변수나 함수의 타입을 명시적으로 지정하지 않은 경우, 해당 변수나 함수는 any 타입으로 처리됩니다. 하지만 noImplicitAny 옵션이 활성화되면, 이러한 암시적인 any 타입 사용을 허용하지 않는다. 따라서 변수나 함수의 타입을 명시적으로 지정하도록 강제한다.
2. strictNullChecks
- strictNullChecks 옵션은 TypeScript에서 null 및 undefined 값을 사용할 때 엄격한 타입 체크를 수행하는 옵션이다.
- 이 옵션이 활성화되면, null 및 undefined 값이 있는 변수를 다른 타입의 변수에 할당할 수 없으며, null 및 undefined 값을 갖는 변수를 사용할 때 추가적인 체크를 수행한다.
아이템 3. 코드 생성과 타입이 관계없음을 이해하기
타입스크립트 컴파일러는 다음의 두 가지 역할을 수행한다.
1. 최신 타입스크립트/자바스크립트를 구버전의 자바스크립트로 트랜스파일 한다.
2. 코드의 타입 오류를 체크한다.
이 두가지는 완전히 독립적으로 실행된다.
따라서, 타입 오류가 있는 코드도 컴파일이 가능하다.
문제가 될 만한 부분을 알려주긴 하지만, 빌드를 멈추지는 않는 것이다.
=> 타입 오류가 있는 코드도 컴파일이 가능하다.
interface Square {
width: number;
}
interface Rectangle extends Square {
height: number;
}
type Shape = Square | Rectangle
function calcuateArea(shape: Shape) {
if (shape instanceof Rectangle) { // 'Rectangle'은(는) 형식만 참조하지만, 여기서는 값으로 사용되고 있습니다.
return shape.width * shape.height
}
}
instanceof 연산자는 JavaScript에서 객체의 인스턴스 여부를 확인하는 데 사용되는데, TypeScript에서는 클래스가 아닌 인터페이스나 타입 별칭에 대해서는 사용할 수 없다. (클래스로 만든다면 Rectangle은 타입과 값으로 모두 사용가능하다.)
따라서 instanceof 연산자를 사용하여 타입을 확인하는 것은 허용되지 않는다.
function calcuateArea(shape: Shape) {
if ("height" in shape) {
return shape.width * shape.height;
}
}
에러 없이 사용하고 싶다면, 이렇게 타입 가드를 이용해서 사용하자.
=> 런타임에는 타입 체크가 불가능하다.
function asNumber(val: number | string): number {
return val as number;
}
위 코드는 타입 체커를 통과한다.
=> 타입 연산은 런타임에 영향을 주지 않는다.
- 런타임 타입은 선언된 타입과 다를 수 있다는 것을 항상 명심해야한다.
- 타입스크립트 타입으로는 함수를 오버로드 할 수 없다.
타입과 타입 연산자는 자바스크립트 변환 시점에 제거가 된다.
- 타입스크립트 타입은 런타임 성능에 영향을 주지 않는다.
아이템 4. 구조적 타이핑에 익숙해지기
자바스크립트는 덕 타이핑 기반이다.
어떤 함수의 매개변수 값이 제대로 주어진다면, 그 값이 어떻게 만들어졌는지 신경 쓰지 않고 사용한다.
interface Value {
x: number;
y: number;
}
interface Info {
name: string;
x: number;
y: number;
}
function caculate(v: Value) {
return v.x + v.y;
}
const info: Info = {
name: "info",
x: 3,
y: 4,
};
caculate(info); // 7
info의 구조가 Value의 구조와 호환이 되기때문에 위 코드는 정상 동작한다.
interface Infos {
a: string;
b: string;
c: string;
}
const values = {
a: "a",
b: "b",
c: "c",
};
function handleValue(values: Infos) {
for (const key of Object.keys(values)) {
const value = values[key]; // 타입선언을 해줬지만 value의 타입은 any
console.log(value);
}
}
타입 선언을 다 해주었지만, value의 추론 타입은 any이다.
왜일까?? 오류인가?
타입스크립트는 타입의 확장에 열려있기때문에 위는 정상 동작하는 것이다.
사실 value는 string 타입이라고 단정할 수 없다.
타입스크립트는 자바스크립트의 모델링을 따른다고 앞에서 말했다.
위의 예제에서 values 객체의 모든 속성은 문자열 타입이지만, 자바스크립트에서는 속성 값에 어떤 값이든 할당할 수 있다.
결국 실행되는건 자바스크립트이기 때문에 타입스크립트는 동적 속성 접근 시 객체의 모든 속성이 지정된 타입을 가질 것이라고 가정하지 않는다.
이러한 이유로 위와같은 동적 속성 접근 시 타입스크립트는 해당 속성에 대해 any 타입으로 추론하는 것이다.
아이템 5. any 타입 지양하기
any는 다음의 위험성이 존재한다.
타입 안정성이 없다.
let age = '12' as any;
age += 1; // "121"
함수 시그니처를 무시한다.
function caculateAge(birthDate: Date): number {
// ...
}
let birthDate: any = '1990-01-01';
caculateAge(birthDate);
언어 서비스가 적용되지 않는다.
ex) 자동완성 기능, 이름 변경 기능...
코드 리팩터링 때 버그를 감춘다.
- 타입체커를 무력화 시킴.
타입 설계를 감춰버린다.
- 객체 상태 정의를 간편하게 만들어주지만, 그만큼 설계에 대한 정확성을 떨어트린다.
타입시스템의 신뢰도를 떨어트린다.
- 이는 타입스크립트의 최대 이점을 없애는 것과 같은 행위이므로 가급적이면 사용하지 않는 것을 권한다.
물론, 실무에서 어쩔수 없이 사용해야하는 경우가 있지만 이 또한 점진적으로 타입을 생성하여 코드 내의 any를 최소화하는 것이 좋다.
'프로그래밍언어 > TypeScript' 카테고리의 다른 글
이펙티브 타입스크립트 스터디 - 7주차 발표 정리 (0) | 2024.06.22 |
---|---|
이펙티브 타입스크립트 스터디 - 6주차 발표 정리 (1) | 2024.06.16 |
이펙티브 타입스크립트 스터디 - 5주차 발표 정리 (1) | 2024.06.07 |
이펙티브 타입스크립트 스터디 - 4주차 발표 정리 (0) | 2024.06.01 |
이펙티브 타입스크립트 스터디 - 3주차 발표 정리 (0) | 2024.05.25 |