Q&A 큐레이션
1. 타입스크립트 인터페이스에서 string 타입 유형을 제대로 인식하지 못하는 문제?
2. 이미 정의된 타입에 'Property xxx does not exist on type' 오류가 발생해요
3. Typescript에서 as 는 왜 안 좋은가요?
4. typescript에서 useRef.current는 null 체크가 필수인가요?
5. typescript 날짜 계산 오류
foods라는 객체배열을 선언하고 Food 타입을 정의했는데요. foods의 type을 string으로 인식해서 Food type의 type인 'Dairy' | 'Protein' 로 인식을 못하는 문제입니다 ㅜ as Food[] 로 타입 단언 해주면 해결되긴 하는데 타입 단언이 안좋다고 들어서요. 이런 에러코드가 나와요. Types of property 'type' are incompatible. Type 'string' is not assignable to type 'foodType' 아래는 코드입니다.
답변
안녕하세요! 타입스크립트는 기본적으로 type을 명시해주지 않으면 타입 추론을 하게됩니다. 질문자님의 코드를 봤을때 타입스크립트 입장에서는 foods 속 객체들이 Food임을 명확하게 알수없기때문에 타입 추론을 하고 있어요. 동희님 의견처럼 타입스크립트가 에러가 나고있는 코드를 첨부해주시면 답변에 도움이 될 것 같습니다. 우선 제가 예상하기로는 const someFunc = (foods: Food[]) => {...} 형태의 함수가 있을때 someFunc(foods)로 호출하시면 말씀하신 에러가 날 것 같은데요. 이거는 foods를 선언하실때 const foods: Food[] = [...] 로 선언해주시면 해결할 수 있는 문제인것 같네요. 이 케이스에서는 타입 추론 대신 foods에 명확한 타입을 명시해줌으로서 해결하는 방법이에요. 다만 이렇게 타입을 명시해주는게 어쩔 때는 독이 될 수 있어서 케바케로 타입 추론과 명시적인 타입을 혼용해서 쓰는게 좋은것 같아요. ex) 대개 함수의 리턴 타입은 타입 추론으로 하시는게 오히려 좋을수도 있습니다 참고해보시면 좋을 것 같아요 :) - https://www.typescriptlang.org/docs/handbook/type-inference.html
아래 코드와 같이 타입이 정의가 되어있는데 key={cancelActionPayload.action} 의 action에 빨간줄 쳐지고 'Property 'action' does not exist on type' 이라 에러가 뜨는데 제 생각에는 action 필드가 분명 있는 것 같은데 뭘 잘못 생각했을까요?ㅜㅜ
답변
안녕하세요. 제가 아는 선에서 최대한 설명드릴게요! 우선 해당 에러가 나는 이유는 컴퓨터의 입장에서 "action" field는 있을수도 있고, 없을수도 있어서 타입스크립트에서 에러를 뱉어주는거에요. 보시면 CancelActionPayload 타입이 3가지의 형태가 될 수 있는데 마지막 형태에서 action이 존재하지 않죠? 그러면 타입스크립트의 입장에서는 이렇게 생각할거에요. "CancelActionPayload 타입은 첫 번째 형태, 두 번째 형태, 세 번째 형태가 있는데 어느 형태가 될 지 나는 잘 모르겠어." 가 돼요. 사실 어떻게 보면 타입스크립트가 정말 고마운 역할을 해주고 있는거죠. 코딩을 하는 입장에서는 이 상황에 들어오는 "CancelActionPayload"은 action이 무조건 있어! 라고 상상할 수 있지만, 컴퓨터의 입장에서는 action이 없을 수도 있는 가능성이 있기 때문에 컴파일 단계에서 에러를 띄워주는거에요. 이런 상황에서 해결법은 CancelActionPayload 타입을 조금 더 쪼개서 확실하게 타입을 지정해는 방식이 있을 것 같아요. 혹은 타입 가드를 이용해서 해당 코드라인에서 CancelActionPayload의 타입을 확실하게 타입스크립트에게 알려주는 방식도 있을 수 있구요. 구글에 위의 키워드들로 검색을 해보시면 조금 더 자세한 내용들 볼 수 있을거에요! 감사합니다~
오늘 회사에서 코드리뷰를 받았어요. 회사에서는 typescript를 사용합니다. typescript를 처음 써보니까 어렵네요. props의 depth가 너무 깊어서 type을 추론하기가 어려워서 any를 사용하거나 as를 사용해서 코드를 짰습니다. 이 부분에서 코드리뷰에서 받은 코멘트가 as나 any는 좋지 않다라는 의견을 받았네요. 개발자 선배분들은 어떤 점에서 as가 안 좋다고 생각하시나요? 그리고 좋은 수정방법이 있을까요?
답변
1. as는 타입스크립트의 타입 검사 기능을 포기하겠다는 것과 마찬가지 입니다 2. as이후로 타입 불일치 또는 프로퍼티 불일치가 발생해도 컴파일 타임에(또는 IDE에서 실시간으로) 에러를 체킹할 수 없습니다. 더 이상 타입스크립트 컴파일러가 책임지지 않습니다 3. 그러므로 as는 (사용 여부에 따라서) 런타임 에러가 발생할 가능성을 높여줍니다 4. 타입스크립트를 사용하는 주된 이유는 런타임 에러를 컴파일 타임으로 당겨와서 에러를 빨리 수정하자는 것인데 사용 취지에 어긋나는 일이라고 할 수 있겠습니다 5. as를 사용해도 되는 상황은 타입이 확정적인 상황이고 다른 타입이 올 가능성이 없는 상황에 사용할 수는 있겠습니다 6. 서버에서 데이터를 응답받을 때는 zod등의 런타임 타입 체킹 라이브러리를 사용하면 as 를 사용하지 않아도 됩니다 7. 타입이 unknown인 경우도 마찬가지로 런타임 타입 체킹 라이브러리를 사용하면 as 를 사용하지 않아도 됩니다 8. 위의 내용들은 as를 사용하여 몇번 골머리를 겪다보면 저절로 체득하게 됩니다 9. 말이 아닌 몸으로 깨닫고 싶으시면 집에 가셔서 토이 프로젝트를 만드신 후에 모든 타입을 as로 도배해 보세요
useRef를 이용해서 focus를 하도록 하고 있는데 typescript에서 오류가 납니다. 아래 코드와 같습니다. 동작은 잘 하는데 코드의 밑줄이 신경쓰입니다.
답변
안녕하세요! 결론부터 말씀드리면, inputRef.current의 타입은 TextInput이라는 것이 확실시되어야 해요. focus는 null이 아니라 TextInput이라는 타입에 존재하는 메소드이기 때문입니다. (엄밀하게는 null 체크를 해야한다기보다 TextInput이라는 것이 확실시되어야 한다는 말이 정확해요. 왜냐하면 useRef 선언 시점에 null을 넣지 않고 string이나 number같은 타입을 넣어도 focus를 동작하게 할 수는 있거든요. 사실 아무도 그렇게 쓰지도 않고 별로 중요한 얘기도 아니라, 여기서는 그냥 넘어가겠습니다🙂) 따라서 아래와 같이 사용하시면 될 것 같아요. 1번) if문 사용 useEffect(() => { if (isRead && inputRef.current) { inputRef.current.focus(); } }, []); 2번) optional chaining 사용 useEffect(() => { if (isRead) { inputRef.current?.focus(); } }, []); 3번) Type Assertion(타입 단언) 사용 <- 이 부분이 맨 처음 말씀드린 내용인데, 이렇게 쓰는 게 별로 일반적이지는 않습니다. 1번 또는 2번 방법 중에서 골라 쓰시면 될 거 같아요! useEffect(() => { (inputRef.current as TextInput).focus(); }, []); 추가 설명드리면, 질문자님께서 만드신 코드의 if (isRead && inputRef && inputRef.current)에서 inputRef는 null 체크를 할 필요가 없습니다. 왜냐하면 inputRef에는 useRef 선언 당시 React.RefObject<TextInput>라는 타입의 object를 할당하기 때문이에요. inputRef의 타입: React.RefObject<TextInput> inputRef.current의 타입: TextInput 또는 null 혹시 이게 진짜인지 궁금하시면 아래 예시 코드와 같이 로그를 한 번 찍어보세요! import { useEffect, useRef } from "react"; const App = () => { const inputRef = useRef<HTMLInputElement>(null); console.log("실제 DOM에 마운트되기 전이에요."); console.log(inputRef); console.log(inputRef.current); useEffect(() => { console.log("실제 DOM에 마운트된 후에요."); console.log(inputRef); console.log(inputRef.current); }, []); return <input ref={inputRef} />; }; export default App; 아마 아래처럼 로그가 찍힐겁니다. 실제 DOM에 마운트되기 전이에요. { current: null } null 실제 DOM에 마운트된 후에요. { current: input } <input> inputRef는 마운트 전/후 모두 null인 적이 없고 언제나 current를 속성으로 가지고 있는 object네요! 조금이나마 도움이 되셨길 바라며, 혹시라도 더 궁금하신 점 있으시면 댓글 남겨주세요~
지금 가입하면 모든 질문의 답변을 볼 수 있어요!
현직자들의 명쾌한 답변을 얻을 수 있어요.
이미 회원이신가요?
지금 가입하면 모든 질문의 답변을 볼 수 있어요!