any를 쓰면 안되는 이유 → 깔끔, 정확, 명료한 코드 작성을 위해 제대로 된 타입 설계는 필수인데 이 타입 설계가 명확해지지 않아 코드를 읽기 힘들어짐.
편집기에서 rename symbol로 타입의 이름을 변경할 수 있다!?
그럼에도 불구하고 any는 점진적 마이그레이션을 위해 존재해야 함.
더 잘 쓰는 방법
좁은 범위에서 any 사용하기
interface Foo { foo: string; }
interface Bar { bar: string; }
declare function expressionReturningFoo(): Foo;
function processBar(b: Bar) { /* ... */ }
function f1() {
const x: any = expressionReturningFoo(); // Don't do this
processBar(x);
}
function f2() {
const x = expressionReturningFoo();
processBar(x as any); // Prefer this
}
const config1: Config {
a: 1,
b: 2,
c: {
key: value // 에러, Foo 타입에 foo 프로퍼티가 필요하지만 Bar 타입에는 없습니다.
}
}
const config2: Config {
a: 1,
b: 2,
c: {
key: value
} as any // 정상, a,b 프로퍼티 마저 타입체크 안됨
}
const config3: Config {
a: 1,
b: 2,
c: {
key: value as any // 정상, a,b 프로퍼티 마저 타입체크 가능
}
}
구체적으로 변형해서 사용하기
function getLengthBad(array: any) {
return array.length;
}
// better than above.
function getLength(array: any[]) {
return array.length;
}
// 배열의 배열 형태 : any[][]
// value만 모를 때
function paramTest({[key: string]: any}) {
...
}
// ㅊㄱ) object 타입은 속성에 접근할 수 없음.
// 함수의 any
type Function0 = () => any; // 매개변수 0개만 가능
type Function1 = (arg: any) => any; // 매개변수 1개만 가능
type Function2 = (...args: any[]) => any; // 매개변수 몇 개든 가능.
item 40
타입 단언을 불가피하게 사용해야 한다면, 정확한 정의를 가지는 함수 안으로 숨기자.
function shallowObjectEqual<T extends object>(a: T, b: T): boolean {
for (const [k, aVal] of Object.entries(a)) {
if (!(k in b) || aVal !== (b as any)[k]) {
return false;
}
}
return Object.keys(a).length === Object.keys(b).length;
}
item 41
any는 타입을 진화시킴.
function range(start: number, limit: number) {
const out = [];
for (let i = start; i < limit; i++) {
out.push(i);
}
return out; // Return type inferred as number[]
}
하지만 암시적 any 상태인 변수에 어떤 할당도 하지 않고 사용할 경우 암시적 any 오류가 발생하게 됨.
function makeSquares(start: number, limit: number) {
const out = [];
// ~~~ Variable 'out' implicitly has type 'any[]' in some locations
range(start, limit).forEach(i => {
out.push(i * i);
});
return out;
// ~~~ Variable 'out' implicitly has an 'any[]' type
}
따라서 any를 진화시키는 방식보다 명시적 타입 구문을 사용하는 것이 안전한 타입을 유지하는 방법임.
item 42
any 대신 unknown을 사용하자.
unknown에서 원하는 타입으로 변환할 수도 있음.
function processValue(val: unknown) {
if (val instanceof Date) {
val // Type is Date
}
}
//or
function isBook(val: unknown): val is Book {
return (
typeof(val) === 'object' && val !== null &&
'name' in val && 'author' in val
);
}
function processValue(val: unknown) {
if (isBook(val)) {
val; // Type is Book
}
}
{} 타입은 null과 undefined를 제외한 모든 값을 포함함.
object 타입은 모든 non-primitive 타입으로 이루어짐. ex) 객체, 배열
unknown 타입이 도입되기 전에 {}가 일반적으로 사용되었음. 이젠 아님.
item 43
몽키 패치보다는 안전한 타입을 사용하자.
몽키 패치 : 런타임 중인 프로그램 메모리의 소스 내용을 직접 바꾸는 것.
내장 타입에 데이터를 저장해야 하는 경우, 보강이나 사용자 정의 인터페이스로 단언하는 방법을 사용하자.
// 보강
export {};
declare global {
interface Document {
/** Genus or species of monkey patch */
monkey: string;
}
}
document.monkey = 'Tamarin'; // OK
// 사용자 정의 인터페이스로 단언
interface MonkeyDocument extends Document {
/** Genus or species of monkey patch */
monkey: string;
}
(document as MonkeyDocument).monkey = 'Macaque';
item 44
noImplicitAny를 설정해도 명시적 any 또는 서드파티 타입 선언(@types)을 통해 any 타입은 코드 내 여전히 존재할 수 있음.
npm의 type-cover-age 패키지를 활용해 any를 추적할 수 있음.
type-coverage 실행 시 —detail 플래그를 붙이면, any 타입이 있는 곳을 모두 출력함.