타입스크립트 : 커스텀 타입을 체크하는 방법
원문
https://stackoverflow.com/questions/51528780/typescript-check-typeof-against-custom-type
번역
⛳ 질문
저는 아래와 같은 커스텀 타입을 만들었습니다
export type Fruit = "apple" | "banana" | "grape";
이 타입으로 만약 string이 Fruit 타입의 일부인지 아닌지 확인하고 싶습니다. 어떻게 해야할까요?
아래 코드는 안 먹히더라고요
let myfruit = "pear" if(typeof myfruit = "Fruit") { console.log("My fruit is of type 'Fruit'") }
어떤 답변이든 감사드려요!
👩💻 답변
🎈🎈채택답변🎈🎈
짧은 답변 :
`typeof`를 런타임에서 `interface` 타입의 체크할 요량으로 사용하지 마세요. 이 타입은 컴파일 타임에서만 존재합니다.
그 대신 유저가 정의한 타입 가드 함수를 이러한 타입을 체크하는데 사용할 수 있습니다.
const fruit = ['apple','banana','grape'] as const
type Fruit = (typeof fruit)[number]
const isFruit = (x:any):x is Fruit
let myfruit = 'pear'
if(isFruit(myfruit)){
console.log("My fruit is of type 'Fruit'")
}
긴 답변:
아마도 타입스크립트에서 value와 type의 차이가 혼란스러우실껍니다. typeof 연산자가 관련되어있는 경우에는 더욱 더 말이죠. 아시다시피, 타입스크립트는 자바스크립트에 정적 타이핑 시스템을 추가한 것입니다. 그리고 이런 타입 시스템은 트랜스파일될때 전부 삭제 되지요. 타입스크립트의 문법은 몇몇 표현식과 구문은 값 자체로서 런타임에 남아있지만, 나머지에서는 전부 디자인/컴파일 타입에서 타입을 참조할 요량으로만 남아있는 것입니다. 값들은 (Value) 타입을 가지고 있습니다. 하지만 그들 스스로가 타입인 것은 아닙니다. 중요한 것은, 코드 어딘가에는 컴파일러가 value(값)을 예상하고 표현식을 해석하여 가능하다면 값을 찾아낼 것입니다.
typeof 연산자는 두가지 역할을 합니다. 표현식 typeof x는 항상 x를 값으로서 간주하지만, typeof x 그 자체는 문맥에 따라 value 혹은 type이 됩니다.
let bar ={a:0}
let TypeofBar = typeof bar // the value "object"
type TypeofBar = typeof bar; // the type {a:number}
let TypeofBar = typeof bar 구문은 javascript를 거쳐 스트링을 만드는 javascript typeof 연산자로서 런타임에서 사용됩니다.
하지만 `type TypeofBar = typeof bar`는 지워집니다. 그리고 이것은 타입스크립트가 bar라고 이름지어진 value에 assign 된 정적 타입을 확인하기 위해 타입스크립트 타입 쿼리 연산자를 사용합니다.
당신의 코드에서는
let myfruit = "pear";
if (typeof myfruit === "Fruit") {
// "string" === "Fruit"
console.log("My fruit is of type 'Fruit'")
}
typeof myfruit`은 타입이 아니라 값입니다 따라서 이것은 타입스크립트 타입쿼리 연산자가 아니라 자바스크립트 `typeof` 연산자입니다.이는 항상 값 `"string"`을 뱉어냅니다. 절대로 `Fruit` 혹은 `"Fruit"`이 될 수 없습니다. 런타임에서 삭제되기 때문에 Typescript 쿼리 타입 연산자의 결과를 얻을 수 없습니다.
`typeof` 연산자의 사용은 포기 하셔야하겠습니다.
당신이 할 수 있는 것은 `Fruit`의 알려진 세 스트링 리터럴이 아닌 `myfruit`의 값을 체크하는 방법입니다. 예를 들면, 이렇게요
const fruit = ["apple","banana","grape"] as const;
export type Fruit = (typeof fruit)[number]
여기서 `Fruit`은 당신 스스로 정의한 것과 동일한 것임을 확인할 수 있습니다. 이후 , 타입 테스트에서 당신은 유저-정의 타입 가드를 이와 같이 쓸 수 있습니다.
const is Fruit = (x:any):x is Fruit => fruit.includes(x)
`isFruit()`은 argument가 fruit array에서 찾을 수 있는지 없는지 확인할 수 있는 함수입니다. 잘 동작하는지 확인해봅시다
let myfruit = "pear";
if (isFruit(myfruit)) {
console.log("My fruit is of type 'Fruit'");
}
이 타입 가드는 또한 컴파일러로 하여금 if 조건문의 "then" 구문
에서 `myfruit`은 `Fruit`인것을 알게 합니다. 만약 Fruit만 받는 함수가 있다고 생각해보세요. 그리고 그 값은 Fruit일수도, 아닐수도 있고요.
declare function acceptFruit(f:Fruit):void;
const myfruit = Math.random() < 0.5 ? "pear" : "banana";
함수를 직접적으로 호출할 수는 없지만
acceptFruit(myfruit) // error, myfruit might be "pear"
체킹을 완료한 "then" 절 안에서는 정상적으로 호출할 수 있습니다
if(isFruit(myfruit)){
acceptFruit(myfruit) // okay, myfruit is known to be "banana"
}
아마도 이 것이 당신이 커스텀 타입이 아닌 값을 체크하려고 한 이유일 것으로 판단됩니다. 이렇게 하시면 됩니다.
다시 한번 말하자면 : `typeof`를 쓰면 안됩니다. string이 아닌 것을 비교할 수는 있겠지만,타입 가드와 타입 추론을 이용하여 중복된 코드를 지우고 컴파일러부터 타입 애널리시스에 대한 컨트롤을 얻으세요