리액트에서 타입스크립트를 적용하던 중, 정리가 필요해서, 찾아보니
좋은 문서가 있어서, 벨로그에 옮겼습니다. 문제시 비공개 처리하겠습니다.
원본출처 https://github.com/typescript-cheatsheets/react
// props 유형 선언 - 더 많은 예제는 "구성 요소 props 입력"을 참조하세요.
type AppProps = {
message: string;
}; /* 소비자가 확장할 수 있도록 내보낼 경우 '인터페이스'를 사용합니다. */
// 함수 구성 요소를 선언하는 가장 쉬운 방법; 반환 유형이 유추됩니다.
const App = ({ message }: AppProps) => <div>{message}</div>;
// 실수로 다른 유형을 반환하는 경우 오류가 발생하도록 반환 유형에 주석을 추가할 수 있습니다.
const App = ({ message }: AppProps): JSX.Element => <div>{message}</div>;
// 유형 선언을 인라인할 수도 있습니다. 소품 유형의 이름 지정을 제거하지만 반복적으로 보입니다.
const App = ({ message }: { message: string }) => <div>{message}</div>;
형식 유추는 간단한 값에 대해 매우 잘 작동합니다.:
const [val, toggle] = React.useState(false);
그러나 많은 후크가 null-ish 기본값으로 초기화되며 유형을 제공하는 방법이 궁금할 수 있습니다. 명시적으로 유형을 선언하고 공용체 유형을 사용합니다.
const [user, setUser] = React.useState<IUser | null>(null);
// later...
setUser(newUser);
상태가 설정 직후 초기화되고 항상 다음 값을 갖는 경우 유형 어설션을 사용할 수도 있습니다.
const [user, setUser] = React.useState({} as IUser);
// later...
setUser(newUser);
이것은 {}유형 의 TypeScript 컴파일러에 일시적으로 "거짓" IUser입니다. user상태 를 설정하여 후속 조치 를 취해야 합니다. 그렇지 않으면 코드의 나머지 부분 user이 유형에 IUser따라 런타임 오류가 발생할 수 있다는 사실에 의존 할 수 있습니다.
useEffect와 useLayoutEffect는 모두 side effect을 수행하고 선택적 정리 함수를 반환하는 데 사용됩니다.
이 함수는 반환 값을 처리하지 않으며 유형이 필요하지 않습니다.
useEffect를 사용할 때는 함수나 정의되지 않은 것 외에 다른 것을 반환하지 않도록 주의하십시오.
그렇지 않으면 TypeScript와 React 둘 다 에러가 날 것입니다.
화살표 기능을 사용할 경우 다음과 같은 문제가 발생할 수 있습니다.
function DelayedEffect(props: { timerMs: number }) {
const { timerMs } = props;
useEffect(
() =>
setTimeout(() => {
/* do stuff */
}, timerMs),
[timerMs]
);
// 나쁜 예! setTimeout은 암시적으로 숫자를 반환합니다.
// 화살표 함수 본문이 중괄호로 묶이지 않았기 때문에
return null;
}
위의 예에 대한 해결책
function DelayedEffect(props: { timerMs: number }) {
const { timerMs } = props;
useEffect(() => {
setTimeout(() => {
/* do stuff */
}, timerMs);
}, [timerMs]);
// 더 나은; void 키워드를 사용하여 정의되지 않은
return null;
}
function Foo() {
// - 가능하면 가능한 한 구체적인 것을 선호합니다. 예를 들어 HTMLDivElement
//는 HTMLElement보다 낫고 Element보다 훨씬 낫습니다.
// - 기술적으로 이것은 RefObject<HTMLDivElement>를 반환합니다.
const divRef = useRef<HTMLDivElement>(null);
useEffect(() => {
// ref.current는 null일 수 있습니다. 이는
// 조건부로 ref-ed 요소를 렌더링하거나
if (!divRef.current) throw Error("divRef is not assigned");
/ // 이제 divRef.current는 HTMLDivElement
doSomethingWith(divRef.current);
});
// React가 관리할 수 있도록 요소에 대한 참조를 제공합니다.
return <div ref={divRef}>etc</div>;
}
function Foo() {
// 기술적으로 이것은 MutableRefObject를 반환합니다.<숫자 | null>
const intervalRef = useRef<number | null>(null);
// 참조를 직접 관리합니다(그래서 MutableRefObject라고 합니다!)
useEffect(() => {
intervalRef.current = setInterval(...);
return () => clearInterval(intervalRef.current);
}, []);
return <button onClick={/* clearInterval the ref */}>Cancel timer</button>;
}
export function useLoading() {
const [isLoading, setState] = React.useState(false);
const load = (aPromise: Promise<any>) => {
setState(true);
return aPromise.finally(() => setState(false));
};
return [isLoading, load] as const;
// (boolean | typeof load)[] 대신 [boolean, typeof load]를 추론 }
}
type AppProps = {
message: string;
count: number;
disabled: boolean;
/** 유형의 배열! */
names: string[];
/** 정확한 문자열 값을 지정하는 문자열 리터럴, 함께 결합하는 공용체 유형 사용 */
status: "waiting" | "success";
/** 속성을 사용하지 않는 한 모든 객체(일반적이지는 않지만 자리 표시자로 유용함) */
obj: object;
obj2: {}; // `object`와 거의 동일, `Object`와 정확히 동일 /** 속성의 수에 제한이 없는 객체 (PREFERRED) */
obj3: {
id: string;
title: string;
};
/** 객체 배열! (공통) */
objArr: {
id: string;
title: string;
}[];
/** 같은 유형의 속성을 가진 dict 객체 */
dict1: {
[key: string]: MyTypeHere;
};
dict2: Record<string, MyTypeHere>;// dict1과 동일
/** 호출하지 않는 한 모든 함수(권장하지 않음) */
onSomething: Function;
/** 아무것도 받지 않거나 반환하지 않는 함수 (매우 일반적임) */
onClick: () => void;
/** 이름이 지정된 prop이 있는 함수(매우 일반적) */
onChange: (id: number) => void;
/** 이벤트를 받는 대체 함수 유형 구문(매우 일반적임 ) */
onClick(event: React.MouseEvent<HTMLButtonElement>): void;
/** 선택적 소품(매우 일반적입니다!) */
optional?: OptionalType;
};
export declare interface AppProps {
children1: JSX.Element; / 나쁜, 배열을 고려하지
children2: JSX.Element | JSX.Element[]; // meh, 문자열을 허용하지 않습니다
children3: React.ReactChildren; // 이름에도 불구하고 전혀 적절한 유형이 아닙니다.
그것은 유틸리티
children4: React.ReactChild[]; // 더 나은, 배열 자식을 받아들입니다.
children: React.ReactNode; // 최상, 모든 것을 수용합니다(아래의 경우 참조)
functionChildren: (name: string) => React.ReactNode; // 자식 render prop type
style?: React.CSSProperties; // 스타일 소품을 전달하기 위해
onChange?: React.FormEventHandler<HTMLInputElement>; // 이벤트를 형성합니다! 일반 매개변수는 event.target의 유형입니다.
// 추가 정보: https://react-typescript-cheatsheet.netlify.app/docs/advanced/patterns_by_usecase/#wrappingmirroring
props: Props & React.ComponentPropsWithoutRef<"button">;
// 버튼 요소의 모든 props를 가장하고 ref
props2: Props & React.ComponentPropsWithRef<MyButtonWithForwardRef>;
// MyButtonForwardedRef의 모든 소품을 가장하고 해당 ref를 명시적으로 전달하기 위해
}
작은 React.ReactNode
엣지 케이스
이 코드는 유형을 검사하지만 런타임 오류가 있습니다.
type Props = {
children: React.ReactNode;
};
function Comp({ children }: Props) {
return <div>{children}</div>;
}
function App() {
return <Comp>{{}}</Comp>; // 런타임 오류: 객체가 React Child로 유효하지 않습니다! Child!
}
이는 ReactNode에 너무 넓은 {} 유형을 허용하는 ReactFragment가 포함되어 있기 때문입니다. 이를 수정하면 많은 라이브러리가 깨질 수 있으므로 현재로서는 ReactNode가 완전히 방탄이 되는 것은 아니라는 점을 유념해야 합니다.
JSX.Element 대 React.ReactNode?
좀 더 기술적인 설명은 유효한 React 노드가 React.createElement에서 반환되는 노드와 동일하지 않다는 것입니다. 구성 요소의 렌더링 결과에 관계없이 React.createElement는 항상 JSX라는 개체를 반환합니다.요소 인터페이스이지만 반응합니다.ReactNode는 구성요소의 가능한 모든 반환 값의 집합입니다.
JSX.Element -> Return value of React.createElement
React.ReactNode -> Return value of a component
유형 또는 인터페이스를 사용하여 Props 및 State를 입력할 수 있으므로 자연스럽게 질문이 생깁니다. 어떤 것을 사용합니까?
Type- orta가 필요할 때까지 Interface를 사용하십시오 .
더 자세한 칼럼을 읽고 싶다면
https://medium.com/@martin_hotell/interface-vs-type-alias-in-typescript-2-7-2a8f1777af4c
다음은 유용한 경험 법칙입니다.
interface라이브러리 또는 타사 앰비언트 유형 정의를 작성할 때 항상 공용 API의 정의에 사용 합니다. 이렇게 하면 일부 정의가 누락된 경우 소비자가 선언 병합을 통해 이를 확장할 수 있습니다 .
type일관성과 더 제한적이기 때문에 React Component Props 및 State에 사용 하는 것을 고려 하십시오.
성능이 문제가 아닌 경우(일반적으로 그렇지 않습니다!) 처리기를 인라인하는 것이 가장 쉽습니다. 유형 추론 및 컨텍스트 입력을 사용할 수 있기 때문입니다 .
const el = (
<button
onClick={(event) => {
/* event will be correctly typed automatically! */
}}
/>
);
그러나 이벤트 핸들러를 별도로 정의해야 하는 경우 @type 정의가
풍부한 입력과 함께 제공되므로 IDE 도구가 여기에서 매우 유용합니다.
찾고 있는 것을 입력하면 일반적으로 자동 완성이 도움이 될 것입니다.
다음은 onChange양식 이벤트의 경우입니다.
type State = {
text: string;
};
class App extends React.Component<Props, State> {
state = {
text: "",
};
// typing on RIGHT hand side of =
onChange = (e: React.FormEvent<HTMLInputElement>): void => {
this.setState({ text: e.currentTarget.value });
};
render() {
return (
<div>
<input type="text" value={this.state.text} onChange={this.onChange} />
</div>
);
}
}
React.FormEvent<> 와 void 대신에 이벤트 핸들러 자체에 형식을 적용할 수도 있습니다.
onChange: React.ChangeEventHandler<HTMLInputElement> = (e) => {
this.setState({text: e.currentTarget.value})
}
같은 일을 두 가지 방법으로 수행하는 이유는 무엇입니까?
첫 번째 방법은 유추된 메서드 서명 (e: React.FormEvent<HTMLInputElement>): void을 사용하고
두번째 방법은 @types/react 에서 제공하는 것을 더 장인이 직접 손으로
만든 거라 할 수 있습니다. 둘다 유용하므로 둘다 알아두면 좋습니다.
이벤트 유형에 신경 쓰지 않는다면 React.SyntheticEvent를 사용할 수 있습니다.
대상 양식에 액세스하려는 사용자 지정 명명된 입력이 있는 경우 유형 어설션을 사용할 수 있습니다.
<f orm
ref={formRef}
onSubmit={(e: React.SyntheticEvent) => {
e.preventDefault();
const target = e.target as typeof e.target & {
email: { value: string };
password: { value: string };
};
const email = target.email.value; // typechecks!
const password = target.password.value; // typechecks!
// etc...
}}
>
<div>
<label>
Email:
<input type="email" name="email" />
</label>
</div>
<div>
<l abel>
Password:
<input type="password" name="password" />
</label>
</div>
<div>
<input type="submit" value="Log in" />
</div>
</f orm>
물론 중요한 형태를 만들고 있다면 TypeScript 로 작성된 Formik 이나 React Hook Form 을 사용해야 합니다.
AnimationEvent CSS Animations.
ChangeEvent
<input>, <select> 와 <textarea> 의 값을 변겨합니다.
ClipboardEvent Using copy, paste and cut events.
CompositionEvent
사용자가 간접적으로 텍스트를 입력하여 발생하는 이벤트
(예: 브라우저 및 PC 설정에 따라 미국 키보드에서 일본어를
입력하려는 경우 추가 문자와 함께 팝업 창이 나타날 수 있음)
DragEvent
FocusEvent
FormEvent
폼 또는 폼 요소가 포커스를 얻거나 잃을 때,
폼 요소 값이 변경되거나 폼이 제출될 때마다 발생하는 이벤트입니다.
InvalidEvent
입력의 유효성 제한이 실패할 때 발생합니다
(예: <input type="number" max="10">누군가가 숫자 20을 삽입할 경우).
마우스, 터치 및 포인터 이벤트에 대한 기본 이벤트입니다.
MouseEvent
사용자가 포인팅 장치(예: 마우스)와 상호 작용하여 발생하는 이벤트
PointerEvent
사용자가 마우스, 펜/스타일러스, 터치스크린과 같은
다양한 포인팅 장치를 사용하여 발생하는 이벤트로 멀티 터치도 지원합니다.
이전 브라우저(IE10 또는 Safari 12)용으로 개발하지 않는 한
포인터 이벤트를 사용하는 것이 좋습니다. UIEvent를 확장합니다.
TouchEvent
사용자가 터치 장치와 상호 작용하여 발생하는 이벤트입니다. UIEvent를 확장합니다.
TransitionEvent
CSS 전환. 브라우저가 완전히 지원되지 않습니다. UIEvent 확장
UIEvent
마우스, 터치 및 포인터 이벤트에 대한 기본 이벤트입니다.
WheelEvent
마우스 휠 또는 이와 유사한 입력 장치에서 스크롤.
(참고: wheel이벤트와 scroll이벤트를 혼동해서는 안 됩니다. )
SyntheticEvent
위의 모든 이벤트에 대한 기본 이벤트입니다.
이벤트 유형이 확실하지 않을 때 사용해야 합니다.
forwardRef/createRef
createRef:
class CssThemeProvider extends React.PureComponent<Props> {
private rootRef = React.createRef<HTMLDivElement>(); // like this
render() {
return <div ref={this.rootRef}>{this.props.children}</div>;
}
}
forwardRef:
type Props = { children: React.ReactNode; type: "submit" | "button" };
export type Ref = HTMLButtonElement;
export const FancyButton = React.f orwardRef<Ref, Props>((props, ref) => (
<b utton ref={r ef} className="MyClassName" type={props.type}>
{props.children}
</b utton>
));
```
lass App extends React.Component<
{},
{
count: number | null; // 이와 같이
}
{
state = {
count: null,
};
render() {
return <div onClick={() => this.increment(1)}>{this.state.count};
}
increment = (amt: number) => {
this.setState((state) => ({
count: (state.count || 0) + amt,
}));
};
}
interface Admin {
role: string;
}
interface User {
email: string;
}
// 방법 1: in
키워드 사용
function redirect(user: Admin | User) {
if ("role" in user) {
// use the in
operator for typeguards since TS 2.7+
routeToAdminPage(user.role);
} else {
routeToHomePage(user.email);
}
}
// 방법 2: 사용자 정의 유형 가드, 이전 TS 버전 또는 in
이 충분하지 않은 경우 동일한 작업을 수행합니다.
function isAdmin(user: Admin | User): user is Admin {
return (user as any).role !== undefined;
}
방법 2는 사용자 정의 유형 보호(User-Defined Type Guard) 라고도 하며
가독성 있는 코드에 매우 유용할 수 있습니다.
이것이 TS 자체가 typeof및 로 유형을 구체화하는 방법 instanceof입니다.
대신 if...else체인이나 switch명령문 이 필요한 경우 "그냥 작동"해야 하지만
도움이 필요한 경우 Discriminated Unions를 찾으십시오 .
(참조: Basarat의 기록 ).
이것은 useReducer또는 Redux에 대한 감속기를 입력할 때 편리합니다 .
```
사용자 정의 유형 가드
일단 체크를 하고 나면 pet각 브랜치 내에서 의 타입을 알 수 있다면 훨씬 좋을 것 입니다.
너무 타이프 라이터가이라는 것을 가지고 발생 유형 가드 . 유형 보호는 일부 범위에서 유형을 보장하는 런타임 검사를 수행하는 일부 표현식입니다.
유형 술어 사용
유형 보호를 정의하려면 반환 유형이 유형 술어 인 함수를 정의하기만 하면 됩니다 .
function isFish(pet: Fish | Bird): pet is Fish {
return (pet as Fish).swim !== undefined;
}
pet is Fish이 예제에서 우리의 유형 술어입니다. 술어는 형식을 취합니다 parameterName is Type. 여기서 parameterName은 현재 함수 서명의 매개변수 이름이어야 합니다.
isFish어떤 변수와 함께 호출 될 때마다 TypeScript는 원래 유형이 호환되는 경우 해당 변수를 특정 유형으로 좁힙니다 .
// Both calls to 'swim' and 'fly' are now okay.
let pet = getSmallPet();
if (isFish(pet)) {
pet.swim();
} else {
pet.fly();
}
타이프 라이터가 알고 할뿐만주의 petA는 Fish에 if지점;
또한 else브랜치 에는 a 가 없다는 것을 알고 Fish있으므로 a 가 있어야 합니다 Bird.
유형 가드 isFish를 사용하여 의 배열을 필터링하고 의 배열을 Fish | Bird
얻을 수 있습니다 Fish.
const zoo: (Fish | Bird)[] = [getSmallPet(), getSmallPet(), getSmallPet()];
const underWater1: Fish[] = zoo.filter(isFish);
// or, equivalently
const underWater2: Fish[] = zoo.filter<Fish>(isFish);
const underWater3: Fish[] = zoo.filter<Fish>((pet) => isFish(pet));
Argument of type '(pet: Fish | Bird) => boolean' is not assignable to parameter of type '(value: Fish | Bird, index: number, array: (Fish | Bird)[]) => value is Fish'.
Signature '(pet: Fish | Bird): boolean' must be a type predicate.
in
연산자 사용
in연산자는 타입 좁아 식으로 작용한다.
A에 대한 n in x표현, n문자열 리터럴 또는 문자열 리터럴 유형과 x노동 조합 유형,
옵션 또는 필요한 속성이 유형에 "true"로 지점 좁아이다
n, 및 선택적 또는 누락이 유형에 "거짓"지점 좁아 재산 n.
function move(pet: Fish | Bird) {
if ("swim" in pet) {
return pet.swim();
}
return pet.fly();
}
class MyComponent extends React.Component<{
message?: string; // like this
}> {
render() {
const { message = "default" } = this.props;
return <div>{message}</div>;
}
}
가능한한 열거형을 사용하지 않는 것이 좋습니다 .
열거형에는 몇 가지 문서화된 문제가 있습니다 (TS 팀이 동의함 ). 열거형에 대한 더 간단한 대안은 문자열 리터럴의 공용체 유형을 선언하는 것입니다.
export declare type Position = "left" | "right" | "top" | "bottom";
열거형을 사용해야 하는 경우 TypeScript의 열거형은 기본적으로 숫자를 사용한다는 점을 기억하십시오. 일반적으로 대신 문자열로 사용하고 싶을 것입니다.
export enum ButtonSizes {
default = "default",
small = "small",
large = "large",
}
// usage
export const PrimaryButton = (
props: Props & React.HTMLProps<HTMLButtonElement>
) => <Button size={ButtonSizes.default} {...props} />;
Type Assertion
때로는 사용 중인 유형이 생각보다 좁거나 다른 API와 함께 작동하기 위해 유니온 유형을 보다 구체적인 유형으로 주장해야 하므로 as키워드를 사용하여 주장해야 한다는 것을 TypeScript보다 더 잘 알고 있는 경우가 있습니다. 이것은 컴파일러에게 당신이 자신보다 더 잘 알고 있음을 알려줍니다.
class MyComponent extends React.Component<{
message: string;
}> {
render() {
const { message } = this.props;
return (
<Component2 message={message as SpecialMessageType}>{message}</Component2>
);
}
}
특히 함수와 관련하여 유니온 유형 대신 오버로드해야 할 수도 있습니다.
함수 유형을 작성하는 가장 일반적인 방법은 다음과 같은 약어를 사용합니다.
type FunctionType1 = (x: string, y: number) => number;
그러나 이것은 Overloading를 허용하지 않습니다.
implementation이 있는 경우 function 키워드를 사용하여 서로 뒤에 배치할 수 있습니다.
```
function pickCard(x: { suit: string; card: number }[]): number;
function pickCard(x: number): { suit: string; card: number };
function pickCard(x): any {
// implementation with combined signature
// ...
}
그러나 구현이 없고 .d.ts정의 파일 만 작성하는 경우에도 도움이 되지 않습니다.
이 경우 속기를 생략하고 구식 방식으로 작성할 수 있습니다.
여기서 기억해야 할 핵심은 TypeScript에 관한 한 다음과
functions are just callable objects with no key
같습니다.
type pickCard = {
(x: { suit: string; card: number }[]): number;
(x: number): { suit: string; card: number };
// no need for combined signature in this form
// you can also type static properties of functions here eg `pickCard.wasCalled`
};
실제 오버로드된 함수를 구현할 때 구현에서 처리할 결합된 호출 서명을 선언해야 하며 이는 유추되지 않습니다. DOM API에서 오버로드의 예를 쉽게 볼 수 있습니다
TypeScript의 유형 유추에 의존하는 것은 훌륭합니다...
유추된 유형이 필요하다는 것을 깨닫고 다시 돌아가서
유형/인터페이스를 명시적으로 선언하여 재사용을 위해 내보낼 수 있을 때까지입니다.
다행히 , 를 사용 typeof하면 그렇게 할 필요가 없습니다.
아무 값이나 사용하십시오.
const [state, setState] = React.useState({
foo: 1,
bar: 2,
}); // {foo: number, bar: number}로 유추된 상태 유형
const someMethod = (obj: typeof state) => {
// 유추되었지만 상태 유형 파악
// obj를 사용하는 일부 코드
setState(obj); // 작동합니다
};
슬라이싱 상태 및 소품 작업은 React에서 일반적입니다.
다시 말하지만, Partial제네릭 유형 을 사용하는 경우 유형을 명시적으로
재정의할 필요가 없습니다 .
const [state, setState] = React.useState({
foo: 1,
bar: 2,
}); // 상태 유형은 {foo: number, bar: number}로 추론됨
// 참고: 오래된 상태 병합은 React.useState에서 실제로 권장되지 않습니다.
/// 여기서는 Partial을 사용하는 방법을 시연하는 것뿐입니다.
const partialStateUpdate = (obj: Partial<typeof state>) =>
setState({ ...state, ...obj });
// later on...
partialStateUpdate({ foo: 2 }); // 이것은 작동합니다
이것은 성가실 수 있지만 여기에 유형을 잡는 방법이 있습니다!
구성 요소의 Prop 유형 가져오기:
React.ComponentProps및 사용 typeof및 선택적으로 Omit겹치는 유형
import { Button } from "library"; // 그러나 ButtonProps를 내보내지 않습니다! oh no!
type ButtonProps = React.ComponentProps<typeof Button>; // 문제 없어요!
type AlertButtonProps = Omit<ButtonProps, "onClick">; // 수정
const AlertButton: React.FC<AlertButtonProps> = (props) => (
<Button onClick={() => alert("hello")} {...props} />
);
ComponentPropsWithoutRef(ComponentProps 대신) 및
ComponentPropsWithRef(귀하의 구성 요소가 특별히 참조를 전달하는 경우 )
사용할 수도 있습니다.
함수의 반환 유형 파악: 다음을 사용합니다 ReturnType
// 일부 라이브러리 내부 - 반환 유형 { baz: number }가 유추되었지만 내보내지지 않았습니다.
function foo ( bar : string ) {
return { baz : 1 } ;
}
// 앱 내부에서 필요한 경우 { baz: number }
type FooReturn = ReturnType < typeof foo > ; // { 바즈: 숫자 }
사실 공개된 거의 모든 것을 가져올 수 있습니다.
Ivan Koshelev의 이 블로그 게시물을 참조하세요 .
function foo() {
return {
a: 1,
b: 2,
subInstArr: [
{
c: 3,
d: 4,
},
],
};
}
type InstType = ReturnType<typeof foo>;
type SubInstArr = InstType["subInstArr"];
type SubIsntType = SubInstArr[0];
let baz: SubIsntType = {
c: 5,
d: 6, // 유형 검사 확인!
};
//
한 줄로 작성할 수도 있지만 //하지만 앞으로 읽을 수 있는지 확인하십시오.
//(점프 없이 왼쪽에서 오른쪽으로 한 번 읽는 것으로 이해할 수 있음)
type SubIsntType2 = ReturnType<typeof foo>["subInstArr"][0];
let baz2: SubIsntType2 = {
c: 5,
d: 6, // 유형 검사 ok!
};
TS는 또한 Parameters함수의 매개변수를 추출하기 위한 유틸리티 유형 과 함께 제공됩니다.
더 많은 "사용자 정의"의 경우 infer키워드는 이에 대한 기본 빌딩 블록이지만 익숙해지는 데 약간의 시간이 걸립니다. 위의 유틸리티 유형에 대한 소스 코드와 이 예제 를 보고 아이디어를 얻으십시오. Basarat 도 좋은 비디오를 가지고infer 있습니다.
내보내지 않은 유형이 있는 모듈보다 더 짜증나는 것은 무엇입니까?
유형 이 지정되지 않은 모듈
걱정마! 이 문제를 해결할 수 있는 몇 가지 방법이 있습니다.
Slapping any on everything
더 게으른 방법은 새 형식 선언 파일을 만드는 typedec.d.ts것입니다. 아직 이 파일이 없다면 디렉토리 루트 include에 있는 tsconfig.json파일 의 배열 을 확인하여 TypeScript에서 파일 경로를 확인할 수 있는지 확인하십시오
// inside tsconfig.json
{
// ...
"include": [
"src" // automatically resolves if the path to declaration is src/typedec.d.ts
]
// ...
}
이 파일 내에서 원하는 모듈에 대한 선언 구문(예: my-untypeed-module-)을
선언 파일에 추가합니다.
// inside typedec.d.ts
declare module "my-untyped-module";
오류 없이 작동하는 데 필요한 경우 이 한 줄만으로도 충분합니다.
이 솔루션은 유형이 지정되지 않은 모듈이 몇 개 미만인 경우 해결 방법으로 잘 작동합니다.
자동유형방식이 존재합니다.
TypeScript를 --allowJs및 와 함께 사용 --declaration하여 라이브러리 유형에서 TypeScript의 "최상의 추측"을 볼 수 있습니다 .
이것이 제대로 작동하지 않으면 dts-gen개체의 런타임 모양을 사용 하여 사용 가능한 모든 속성을 정확하게 열거하는 데 사용합니다. 이것은 매우 정확한 경향이 있지만 도구는 아직 추가 유형을 채우기 위해 JSDoc 주석을 스크래핑하는 것을 지원하지 않습니다.
npm install -g dts-gen
dts-gen -m < 귀하의 모듈 >
다른 자동화된 JS에서 TS로의 변환 도구 및 마이그레이션 전략이 있습니다.
MIGRATION 치트시트를 참조하십시오 .
Typing Exported Hooks
Hook을 입력하는 것은 순수 함수를 입력하는 것과 같습니다.
다음 단계는 두 가지 가정 하에 작동합니다.
섹션의 앞부분에서 설명한 대로 유형 선언 파일을 이미 만들었습니다.
소스 코드, 특히 사용할 기능을 직접 내보내는 코드에 액세스할 수 있습니다. 대부분의 경우 index.js파일에 보관 됩니다. 일반적으로 후크를 완전히 정의 하려면 최소 두 가지 유형 선언(하나는 Input Prop용 이고 다른 하나는 Return Prop용 )이 필요합니다. 입력하려는 후크가 다음 구조를 따른다고 가정합니다.
// ...
const useUntypedHook = (prop) => {
// some processing happens here
return {
/* ReturnProps */
};
};
export default useUntypedHook;
그런 다음 유형 선언은 다음 구문을 따라야 합니다.
declare module 'use-untyped-hook' {
export interface InputProps { ... } // type declaration for prop
export interface ReturnProps { ... } // type declaration for return props
export default function useUntypedHook(
prop: InputProps
// ...
): ReturnProps;
}
예를 들면 다음과 같습니다.
// inside src/index.js
const useDarkMode = (
initialValue = false, // -> input props / config props to be exported
{
// -> input props / config props to be exported
element,
classNameDark,
classNameLight,
onChange,
storageKey = "darkMode",
storageProvider,
global,
} = {}
) => {
// ...
return {
// -> return props to be exported
value: state,
enable: useCallback(() => setState(true), [setState]),
disable: useCallback(() => setState(false), [setState]),
toggle: useCallback(() => setState((current) => !current), [setState]),
};
};
export default useDarkMode;
주석에서 알 수 있듯이 이러한 구성 props를 내보내고 앞서 언급한 구조에 따라 props를 반환하면 다음과 같은 유형의 내보내기가 발생합니다.
declare module "use-dark-mode" {
/**
* `useDarkMode`의 특정 측면을 지정할 수 있도록 하는 구성 개체
*/
export interface DarkModeConfig {
classNameDark?: string; // "다크 모드"를 설정하기 위한 className. 기본값 = "다크 모드"입니다.
classNameLight?: string; // "Light 모드"를 설정하기 위한 className. 기본값 = "Light 모드"입니다.
element?: HTMLElement; // className을 적용할 요소. 기본값 = `document.body`
onChange?: (val?: boolean) => void; // 사용자 정의 콜백으로 기본 className 핸들러를 재정의합니다.
storageKey?: string; // `localStorage` 키를 지정합니다. 기본값 = "다크 모드". 영구 저장소를 비활성화하려면 'null'로 설정합니다.
storageProvider?: WindowLocalStorage; // 스토리지 제공자. 기본값 = `localStorage`.
global?: Window; // 전역 객체. 기본값 = `창`.
}
/**
* `useDarkMode` 호출에서 반환된 객체.
*/
export interface DarkMode {
readonly value: boolean;
enable: () => void;
disable: () => void;
toggle: () => void;
}
/**
* 애플리케이션의 "다크 모드" 구성 요소를 구현하는 데 도움이 되는 사용자 지정 React Hook.
*/
export default function useDarkMode(
initialState?: boolean,
config?: DarkModeConfig
): DarkMode;
}
타입이 지정되지 않은 클래스 컴포넌트를 타이핑하는 경우
타입을 선언한 후 타입 선언을 보유하는 class UntypedClassComponent extends React.Component<UntypedClassComponentProps, any> {}where를 사용하여 타입을 익스포트한다는 점을 제외하면 접근 방식에는 거의 차이가 없습니다 UntypedClassComponentProps.
예를 들어, React Router 6 유형에 대한 sw-yx의 Gist는 유형이 지정되지 않은 RR6을 입력하는 유사한 방법을 구현했습니다.
declare module "react-router-dom" {
import * as React from 'react';
// ...
type NavigateProps = {
to: string | number,
replace?: boolean,
state?: T
}
//...
export class Navigate<T = any> extends React.Component<NavigateProps>{}
// ...
TypeScript의 자주 알려진 문제
React 개발자가 자주 접하는 항목의 목록으로,
TS에는 이에 대한 솔루션이 없습니다. 반드시 TSX만 해당되는 것은 아닙니다.
개체 요소 null 검사 후 TypeScript가 좁혀지지 않습니다.