TypeScript React props에 React props를 전문적으로 입력하는 방법 - 노조 유형 구분과 투쟁
75812 단어 reactjavascripttutorialtypescript
TypeScript와 React를 사용하는 모든 사람들은 도구를 어떻게 입력하는지 알고 있죠?
제1부
우리가 A
, B
, C
, props.check
의 세 가지 유효 상태를 가지고 있다고 상상해 보자.
enum Mode {
easy = 'easy',
medium = 'medium',
hard = 'hard'
}
type A = {
mode: Mode.easy;
data: string;
check: (a: A['data']) => string
}
type B = {
mode: Mode.medium;
data: number;
check: (a: B['data']) => number
}
type C = {
mode: Mode.hard;
data: number[];
check: (a: C['data']) => number
}
현재, 우리는 우리의 구성 요소가 유효한 도구만 받아들일 수 있도록 확보해야 한다.
type Props = A | B | C;
const Comp: FC<Props> = (props) => {
if (props.mode === Mode.easy) {
const x = props // A
}
if (props.mode === Mode.medium) {
const x = props // B
}
if (props.mode === Mode.hard) {
const x = props // C
}
return null
}
복잡한 거 없어요. 그렇죠?
현재 조건문 외에 check
을 호출해 보십시오.
const Comp: FC<Props> = (props) => {
props.check(props.data) // error
return null
}
그런데 왜 틀렸을까요?
TL;박사
같은 유형의 변수가 반전 위치에 있는 여러 후보 변수는 교차점 유형을 추정할 수 있습니다.
우리에게는
type Intersection = string & number & number[] // never
이것이 바로 never
기대 js
유형의 원인이다.
Destructure는 TS 유형 추정에 적합하지 않다는 것을 잊지 마십시오.
const Comp: FC<Props> = ({ check, data, mode }) => {
if (mode === Mode.easy) {
check(data) // error
}
return null
}
destructure를 사용하고 싶으면 typeguards를 동시에 사용하십시오.
const isEasy = <M extends Mode>(
mode: M,
check: Fn
): check is Extract<Props, { mode: Mode.easy }>['check'] =>
mode === Mode.easy
기왕 우리가 코드 라이브러리에 추가 기능을 추가한 이상, 우리는 반드시 테스트를 진행해야 한다. 그렇지?
나는 너에게 길을 안내해 주고 싶다. 별도의 수표는 필요 없다.
typeguards를 사용하는 것보다 안전하거나 더 좋다고 말하는 것은 아닙니다.사실은 그렇지 않다.응용 프로그램의 업무 논리를 변경하고 싶지 않으면 이런 방법을 사용할 수 있다.이 변경 후에 단원 테스트를 작성하라고 요구하는 사람이 없습니다:) 상상해 보십시오. ts
에서 check
으로 이동하기만 하면 됩니다.TabsWithState
으로 전화를 걸 수 있도록 적재량을 초과해야 합니다.
우리는 우리의 연습을 다섯 개의 작은 부분으로 나눈다.
1. 속성이 함수인 키 이름을 가져옵니다.
// Get keys where value is a function
type FnProps<T> = {
[Prop in keyof T]: T[Prop] extends Fn ? Prop : never
}[keyof T]
// check
type Result0 = FnProps<Props>
2. 모든 함수의 합집합을 실현한다.
type Values<T> = T[keyof T]
type FnUnion<PropsUnion> = Values<Pick<PropsUnion, FnProps<PropsUnion>>>
// | ((a: A['data']) => string)
// | ((a: B['data']) => number)
// | ((a: C['data']) => number)
type Result1 = FnUnion<Props>
3. 작은 특정 과부하 계산
type ParametersUnion<PropsUnion> =
FnUnion<PropsUnion> extends Fn
? (a: Parameters<FnUnion<PropsUnion>>[0]) =>
ReturnType<FnUnion<PropsUnion>>
: never
// (a: string | number | number[]) => string | number
type Result2 = ParametersUnion<Props>
4. 함수 집합을 다시 불러오기 위해서 우리는 집합이 아니라 집합을 사용해야 한다.그래서 우리는 함수를 합쳐서 구체적이지 않은 재부팅과 합치자
// credits goes to https://stackoverflow.com/a/50375286
type UnionToIntersection<U> = (U extends any ? (k: U) => void : never) extends (
k: infer I
) => void
? I
: never;
type Overload<PropsUnion> =
& UnionToIntersection<PropsUnion[FnProps<PropsUnion>]>
& ParametersUnion<PropsUnion>
// & ((a: A['data']) => string)
// & ((a: B['data']) => number)
// & ((a: C['data']) => number)
// & ((a: string | number | number[]) => string | number)
type Result3 = Overload<Props>
5. 마지막 걸음.우리는 우리의 연합과 중재 기능을 통합시켜야 한다.다시 말하면, 우리는 checkproperty를 덮어쓸 것이다
type OverloadedProps<PropsUnion> =
& PropsUnion
& Record<FnProps<PropsUnion>, Overload<PropsUnion>>
// Props & Record<"check", Overload<Props>>
type Result4 = OverloadedProps<Props>
전체 예:
import React, { FC } from 'react'
enum Mode {
easy = 'easy',
medium = 'medium',
hard = 'hard'
}
type A = {
mode: Mode.easy;
data: string;
check: (a: A['data']) => string
}
type B = {
mode: Mode.medium;
data: number;
check: (a: B['data']) => number
}
type C = {
mode: Mode.hard;
data: number[];
check: (a: C['data']) => number
}
type Fn = (...args: any[]) => any
// credits goes to https://stackoverflow.com/a/50375286
type UnionToIntersection<U> = (U extends any ? (k: U) => void : never) extends (
k: infer I
) => void
? I
: never;
type Props = A | B | C;
// Get keys where value is a function
type FnProps<T> = {
[Prop in keyof T]: T[Prop] extends Fn ? Prop : never
}[keyof T]
// check
type Result0 = FnProps<Props>
type Values<T> = T[keyof T]
type FnUnion<PropsUnion> = Values<Pick<PropsUnion, FnProps<PropsUnion>>>
// | ((a: A['data']) => string)
// | ((a: B['data']) => number)
// | ((a: C['data']) => number)
type Result1 = FnUnion<Props>
type ParametersUnion<PropsUnion> =
FnUnion<PropsUnion> extends Fn
? (a: Parameters<FnUnion<PropsUnion>>[0]) =>
ReturnType<FnUnion<PropsUnion>>
: never
// (a: string | number | number[]) => string | number
type Result2 = ParametersUnion<Props>
type Overload<PropsUnion> =
& UnionToIntersection<PropsUnion[FnProps<PropsUnion>]>
& ParametersUnion<PropsUnion>
// & ((a: A['data']) => string)
// & ((a: B['data']) => number)
// & ((a: C['data']) => number)
// & ((a: string | number | number[]) => string | number)
type Result3 = Overload<Props>
type OverloadedProps<PropsUnion> =
& PropsUnion
& Record<FnProps<PropsUnion>, Overload<PropsUnion>>
// Props & Record<"check", Overload<Props>>
type Result4 = OverloadedProps<Props>
const Comp: FC<OverloadedProps<Props>> = (props) => {
const { mode, data, check } = props;
if (props.mode === Mode.easy) {
props.data // string
}
const result = check(data) // string | number
return null
}
이것은 아이템을 입력하는 잘못된 방식임을 명심하세요.이를 임시 해결 방안으로 간주하다.
제2부분
Stackoverflow의 또 다른 예를 생각해 봅시다.
리액트 아이템 - 차별적 노조 유형과 맞서 싸우다
7월 8일 21일
설명: 1
정답: 4
2
나는 도구와 유사한 두 개의 조립품을 가지고 있지만, 중요한 차이가 하나 있다.tabs
이라는 구성 요소는 하나의 도구 tabs
만 사용합니다. 이것은 다음과 같은 모양의 대상입니다.interface ItemWithState {
name: string
active: boolean;
}
interface WithStateProps {
tabs: ItemWithState[];
};
또 다른 유사한...
Open Full Question
우리는 유사한 도구를 가진 두 개의 구성 요소가 있는데 withRouter
속성은 흔히 볼 수 있는 것이다.
interface ItemWithState {
name: string;
active: boolean;
}
interface ItemWithRouter {
name: string;
path: string;
}
type WithStateProps = {
tabs: ItemWithState[];
};
type WithRouterProps = {
withRouter: true;
baseUrl?: string;
tabs: ItemWithRouter[];
};
const TabsWithRouter: FC<WithRouterProps> = (props) => null
const TabsWithState: FC<WithStateProps> = (props) => null
또한 다음과 같은 고급 구성 요소가 있습니다.
type TabsProps = WithStateProps | WithRouterProps;
const Tabs = (props: TabsProps) => {
if (props.withRouter) { // error
return <TabsWithRouter {...props} />; // error
}
return <TabsWithState {...props} />; // error
};
우리는 마지막으로 세 가지 잘못을 저질렀다.
TS에서는 선택 사항이므로 tabs
속성을 가져올 수 없습니다.반대로 공공 속성 withRouter?:never
만 얻을 수 있습니다.이것은 예기한 행위다.
해결 방법이 하나 있다.WithStateProps
을 {...props}
유형에 추가할 수 있습니다.
현재 그것은 일을 시작하여 Tabs
의 유형을 추정하고 있다.그러나 이것은 작은 단점이 하나 있다. 그것은 우리가 hacks
구성 요소의 불법 도구로 전달할 수 있도록 허락한다.
import React, { FC } from 'react'
interface ItemWithState {
name: string;
active: boolean;
}
interface ItemWithRouter {
name: string;
path: string;
}
type WithStateProps = {
withRouter?: never;
tabs: ItemWithState[];
};
type WithRouterProps = {
withRouter: true;
baseUrl?: string;
tabs: ItemWithRouter[];
};
const TabsWithRouter: FC<WithRouterProps> = (props) => null
const TabsWithState: FC<WithStateProps> = (props) => null
type TabsProps = WithStateProps | WithRouterProps;
const Tabs = (props: TabsProps) => {
if (props.withRouter) {
return <TabsWithRouter {...props} />;
}
return <TabsWithState {...props} />;
};
const Test = () => {
return (
<div>
<Tabs // With incorrect state props
baseUrl="something"
tabs={[{ name: "myname", active: true }]}
/>
</div>
);
};
이런 방법은 매우 나쁘다.다른 typeguard를 시도해 봅시다.
interface ItemWithState {
name: string;
active: boolean;
}
interface ItemWithRouter {
name: string;
path: string;
}
type WithStateProps = {
tabs: ItemWithState[];
};
type WithRouterProps = {
withRouter: true;
baseUrl?: string;
tabs: ItemWithRouter[];
};
const TabsWithRouter: FC<WithRouterProps> = (props) => null
const TabsWithState: FC<WithStateProps> = (props) => null
type TabsProps = WithStateProps | WithRouterProps;
const hasProperty = <Obj, Prop extends string>(obj: Obj, prop: Prop)
: obj is Obj & Record<Prop, unknown> =>
Object.prototype.hasOwnProperty.call(obj, prop);
const Tabs = (props: TabsProps) => {
if (hasProperty(props, 'withRouter')) {
return <TabsWithRouter {...props} />;
}
return <TabsWithState {...props} />;
};
const Test = () => {
return (
<div>
<Tabs // With incorrect state props
baseUrl="something"
tabs={[{ name: "myname", active: true }]}
/>
</div>
);
};
나는 이런 방법이 더 좋다고 믿는다. 왜냐하면 우리는 어떤 WithStateProps
도 사용할 필요가 없기 때문이다.우리의 Animal
형은 어떤 추가 선택 도구도 있어서는 안 된다.그러나 그것은 여전히 같은 결점을 가지고 있다.불법 상태는 허용된다.
함수 재부팅을 잊어버린 것 같습니다.이것은 간단한 함수이기 때문에react 구성 요소의 작업 방식과 같습니다.
함수의 교차는 다시 로드된다는 점에 유의하십시오.
// type Overload = FC<WithStateProps> & FC<WithRouterProps>
const Tabs: FC<WithStateProps> & FC<WithRouterProps> = (props: TabsProps) => {
if (hasProperty(props, 'withRouter')) {
return <TabsWithRouter {...props} />;
}
return <TabsWithState {...props} />;
};
const Test = () => {
return (
<div>
<Tabs // With correct state props
tabs={[{ name: "myname", active: true }]}
/>
<Tabs // With incorrect state props
baseUrl="something"
tabs={[{ name: "myname", active: true }]}
/>
<Tabs // WIth correct router props
withRouter
tabs={[{ name: "myname", path: "somepath" }]}
/>
<Tabs // WIth correct router props
withRouter
baseUrl="someurl"
tabs={[{ name: "myname", path: "somepath" }]}
/>
<Tabs // WIth incorrect router props
withRouter
tabs={[{ name: "myname", active: true }]}
/>
</div>
);
};
문제: 만약에 우리가 연맹에 다섯 개의 요소가 있다면?
A:조건 분포 유형을 사용할 수 있습니다.
import React, { FC } from 'react'
interface ItemWithState {
name: string;
active: boolean;
}
interface ItemWithRouter {
name: string;
path: string;
}
type WithStateProps = {
tabs: ItemWithState[];
};
type WithRouterProps = {
withRouter: true;
baseUrl?: string;
tabs: ItemWithRouter[];
};
const TabsWithRouter: FC<WithRouterProps> = (props) => null
const TabsWithState: FC<WithStateProps> = (props) => null
type TabsProps = WithStateProps | WithRouterProps;
const hasProperty = <Obj, Prop extends string>(obj: Obj, prop: Prop)
: obj is Obj & Record<Prop, unknown> =>
Object.prototype.hasOwnProperty.call(obj, prop);
// credits goes to https://stackoverflow.com/a/50375286
type UnionToIntersection<U> = (U extends any ? (k: U) => void : never) extends (
k: infer I
) => void
? I
: never;
type Distributive<T> = T extends any ? FC<T> : never
type Overload = UnionToIntersection<Distributive<TabsProps>>
const Tabs: Overload = (props: TabsProps) => {
if (hasProperty(props, 'withRouter')) {
return <TabsWithRouter {...props} />;
}
return <TabsWithState {...props} />;
};
const Test = () => {
return (
<div>
<Tabs // With correct state props
tabs={[{ name: "myname", active: true }]}
/>
<Tabs // With incorrect state props
baseUrl="something"
tabs={[{ name: "myname", active: true }]}
/>
<Tabs // WIth correct router props
withRouter
tabs={[{ name: "myname", path: "somepath" }]}
/>
<Tabs // WIth correct router props
withRouter
baseUrl="someurl"
tabs={[{ name: "myname", path: "somepath" }]}
/>
<Tabs // WIth incorrect router props
withRouter
tabs={[{ name: "myname", active: true }]}
/>
</div>
);
};
다음과 같은 방법을 사용할 수도 있습니다.
type Overloading =
& ((props: WithStateProps) => JSX.Element)
& ((props: WithRouterProps) => JSX.Element)
이것은 스타일의 문제다.
나는 네가 아직 피곤하지 않기를 바란다.
제3부분dogName
개의 구성 요소가 다음 구속을 갖는다고 가정합니다.
enum Mode {
easy = 'easy',
medium = 'medium',
hard = 'hard'
}
type A = {
mode: Mode.easy;
data: string;
check: (a: A['data']) => string
}
type B = {
mode: Mode.medium;
data: number;
check: (a: B['data']) => number
}
type C = {
mode: Mode.hard;
data: number[];
check: (a: C['data']) => number
}
type Props = A | B | C;
const Comp: FC<Props> = (props) => {
if (props.mode === Mode.easy) {
const x = props // A
}
if (props.mode === Mode.medium) {
const x = props // B
}
if (props.mode === Mode.hard) {
const x = props // C
}
return null
}
const Comp: FC<Props> = (props) => {
props.check(props.data) // error
return null
}
type Intersection = string & number & number[] // never
const Comp: FC<Props> = ({ check, data, mode }) => {
if (mode === Mode.easy) {
check(data) // error
}
return null
}
const isEasy = <M extends Mode>(
mode: M,
check: Fn
): check is Extract<Props, { mode: Mode.easy }>['check'] =>
mode === Mode.easy
// Get keys where value is a function
type FnProps<T> = {
[Prop in keyof T]: T[Prop] extends Fn ? Prop : never
}[keyof T]
// check
type Result0 = FnProps<Props>
type Values<T> = T[keyof T]
type FnUnion<PropsUnion> = Values<Pick<PropsUnion, FnProps<PropsUnion>>>
// | ((a: A['data']) => string)
// | ((a: B['data']) => number)
// | ((a: C['data']) => number)
type Result1 = FnUnion<Props>
type ParametersUnion<PropsUnion> =
FnUnion<PropsUnion> extends Fn
? (a: Parameters<FnUnion<PropsUnion>>[0]) =>
ReturnType<FnUnion<PropsUnion>>
: never
// (a: string | number | number[]) => string | number
type Result2 = ParametersUnion<Props>
// credits goes to https://stackoverflow.com/a/50375286
type UnionToIntersection<U> = (U extends any ? (k: U) => void : never) extends (
k: infer I
) => void
? I
: never;
type Overload<PropsUnion> =
& UnionToIntersection<PropsUnion[FnProps<PropsUnion>]>
& ParametersUnion<PropsUnion>
// & ((a: A['data']) => string)
// & ((a: B['data']) => number)
// & ((a: C['data']) => number)
// & ((a: string | number | number[]) => string | number)
type Result3 = Overload<Props>
type OverloadedProps<PropsUnion> =
& PropsUnion
& Record<FnProps<PropsUnion>, Overload<PropsUnion>>
// Props & Record<"check", Overload<Props>>
type Result4 = OverloadedProps<Props>
import React, { FC } from 'react'
enum Mode {
easy = 'easy',
medium = 'medium',
hard = 'hard'
}
type A = {
mode: Mode.easy;
data: string;
check: (a: A['data']) => string
}
type B = {
mode: Mode.medium;
data: number;
check: (a: B['data']) => number
}
type C = {
mode: Mode.hard;
data: number[];
check: (a: C['data']) => number
}
type Fn = (...args: any[]) => any
// credits goes to https://stackoverflow.com/a/50375286
type UnionToIntersection<U> = (U extends any ? (k: U) => void : never) extends (
k: infer I
) => void
? I
: never;
type Props = A | B | C;
// Get keys where value is a function
type FnProps<T> = {
[Prop in keyof T]: T[Prop] extends Fn ? Prop : never
}[keyof T]
// check
type Result0 = FnProps<Props>
type Values<T> = T[keyof T]
type FnUnion<PropsUnion> = Values<Pick<PropsUnion, FnProps<PropsUnion>>>
// | ((a: A['data']) => string)
// | ((a: B['data']) => number)
// | ((a: C['data']) => number)
type Result1 = FnUnion<Props>
type ParametersUnion<PropsUnion> =
FnUnion<PropsUnion> extends Fn
? (a: Parameters<FnUnion<PropsUnion>>[0]) =>
ReturnType<FnUnion<PropsUnion>>
: never
// (a: string | number | number[]) => string | number
type Result2 = ParametersUnion<Props>
type Overload<PropsUnion> =
& UnionToIntersection<PropsUnion[FnProps<PropsUnion>]>
& ParametersUnion<PropsUnion>
// & ((a: A['data']) => string)
// & ((a: B['data']) => number)
// & ((a: C['data']) => number)
// & ((a: string | number | number[]) => string | number)
type Result3 = Overload<Props>
type OverloadedProps<PropsUnion> =
& PropsUnion
& Record<FnProps<PropsUnion>, Overload<PropsUnion>>
// Props & Record<"check", Overload<Props>>
type Result4 = OverloadedProps<Props>
const Comp: FC<OverloadedProps<Props>> = (props) => {
const { mode, data, check } = props;
if (props.mode === Mode.easy) {
props.data // string
}
const result = check(data) // string | number
return null
}
Stackoverflow의 또 다른 예를 생각해 봅시다.
리액트 아이템 - 차별적 노조 유형과 맞서 싸우다
7월 8일 21일
설명: 1
정답: 4
2
나는 도구와 유사한 두 개의 조립품을 가지고 있지만, 중요한 차이가 하나 있다.tabs
이라는 구성 요소는 하나의 도구 tabs
만 사용합니다. 이것은 다음과 같은 모양의 대상입니다.interface ItemWithState {
name: string
active: boolean;
}
interface WithStateProps {
tabs: ItemWithState[];
};
또 다른 유사한...
Open Full Question
우리는 유사한 도구를 가진 두 개의 구성 요소가 있는데 withRouter
속성은 흔히 볼 수 있는 것이다.
interface ItemWithState {
name: string;
active: boolean;
}
interface ItemWithRouter {
name: string;
path: string;
}
type WithStateProps = {
tabs: ItemWithState[];
};
type WithRouterProps = {
withRouter: true;
baseUrl?: string;
tabs: ItemWithRouter[];
};
const TabsWithRouter: FC<WithRouterProps> = (props) => null
const TabsWithState: FC<WithStateProps> = (props) => null
또한 다음과 같은 고급 구성 요소가 있습니다.
type TabsProps = WithStateProps | WithRouterProps;
const Tabs = (props: TabsProps) => {
if (props.withRouter) { // error
return <TabsWithRouter {...props} />; // error
}
return <TabsWithState {...props} />; // error
};
우리는 마지막으로 세 가지 잘못을 저질렀다.
TS에서는 선택 사항이므로 tabs
속성을 가져올 수 없습니다.반대로 공공 속성 withRouter?:never
만 얻을 수 있습니다.이것은 예기한 행위다.
해결 방법이 하나 있다.WithStateProps
을 {...props}
유형에 추가할 수 있습니다.
현재 그것은 일을 시작하여 Tabs
의 유형을 추정하고 있다.그러나 이것은 작은 단점이 하나 있다. 그것은 우리가 hacks
구성 요소의 불법 도구로 전달할 수 있도록 허락한다.
import React, { FC } from 'react'
interface ItemWithState {
name: string;
active: boolean;
}
interface ItemWithRouter {
name: string;
path: string;
}
type WithStateProps = {
withRouter?: never;
tabs: ItemWithState[];
};
type WithRouterProps = {
withRouter: true;
baseUrl?: string;
tabs: ItemWithRouter[];
};
const TabsWithRouter: FC<WithRouterProps> = (props) => null
const TabsWithState: FC<WithStateProps> = (props) => null
type TabsProps = WithStateProps | WithRouterProps;
const Tabs = (props: TabsProps) => {
if (props.withRouter) {
return <TabsWithRouter {...props} />;
}
return <TabsWithState {...props} />;
};
const Test = () => {
return (
<div>
<Tabs // With incorrect state props
baseUrl="something"
tabs={[{ name: "myname", active: true }]}
/>
</div>
);
};
이런 방법은 매우 나쁘다.다른 typeguard를 시도해 봅시다.
interface ItemWithState {
name: string;
active: boolean;
}
interface ItemWithRouter {
name: string;
path: string;
}
type WithStateProps = {
tabs: ItemWithState[];
};
type WithRouterProps = {
withRouter: true;
baseUrl?: string;
tabs: ItemWithRouter[];
};
const TabsWithRouter: FC<WithRouterProps> = (props) => null
const TabsWithState: FC<WithStateProps> = (props) => null
type TabsProps = WithStateProps | WithRouterProps;
const hasProperty = <Obj, Prop extends string>(obj: Obj, prop: Prop)
: obj is Obj & Record<Prop, unknown> =>
Object.prototype.hasOwnProperty.call(obj, prop);
const Tabs = (props: TabsProps) => {
if (hasProperty(props, 'withRouter')) {
return <TabsWithRouter {...props} />;
}
return <TabsWithState {...props} />;
};
const Test = () => {
return (
<div>
<Tabs // With incorrect state props
baseUrl="something"
tabs={[{ name: "myname", active: true }]}
/>
</div>
);
};
나는 이런 방법이 더 좋다고 믿는다. 왜냐하면 우리는 어떤 WithStateProps
도 사용할 필요가 없기 때문이다.우리의 Animal
형은 어떤 추가 선택 도구도 있어서는 안 된다.그러나 그것은 여전히 같은 결점을 가지고 있다.불법 상태는 허용된다.
함수 재부팅을 잊어버린 것 같습니다.이것은 간단한 함수이기 때문에react 구성 요소의 작업 방식과 같습니다.
함수의 교차는 다시 로드된다는 점에 유의하십시오.
// type Overload = FC<WithStateProps> & FC<WithRouterProps>
const Tabs: FC<WithStateProps> & FC<WithRouterProps> = (props: TabsProps) => {
if (hasProperty(props, 'withRouter')) {
return <TabsWithRouter {...props} />;
}
return <TabsWithState {...props} />;
};
const Test = () => {
return (
<div>
<Tabs // With correct state props
tabs={[{ name: "myname", active: true }]}
/>
<Tabs // With incorrect state props
baseUrl="something"
tabs={[{ name: "myname", active: true }]}
/>
<Tabs // WIth correct router props
withRouter
tabs={[{ name: "myname", path: "somepath" }]}
/>
<Tabs // WIth correct router props
withRouter
baseUrl="someurl"
tabs={[{ name: "myname", path: "somepath" }]}
/>
<Tabs // WIth incorrect router props
withRouter
tabs={[{ name: "myname", active: true }]}
/>
</div>
);
};
문제: 만약에 우리가 연맹에 다섯 개의 요소가 있다면?
A:조건 분포 유형을 사용할 수 있습니다.
import React, { FC } from 'react'
interface ItemWithState {
name: string;
active: boolean;
}
interface ItemWithRouter {
name: string;
path: string;
}
type WithStateProps = {
tabs: ItemWithState[];
};
type WithRouterProps = {
withRouter: true;
baseUrl?: string;
tabs: ItemWithRouter[];
};
const TabsWithRouter: FC<WithRouterProps> = (props) => null
const TabsWithState: FC<WithStateProps> = (props) => null
type TabsProps = WithStateProps | WithRouterProps;
const hasProperty = <Obj, Prop extends string>(obj: Obj, prop: Prop)
: obj is Obj & Record<Prop, unknown> =>
Object.prototype.hasOwnProperty.call(obj, prop);
// credits goes to https://stackoverflow.com/a/50375286
type UnionToIntersection<U> = (U extends any ? (k: U) => void : never) extends (
k: infer I
) => void
? I
: never;
type Distributive<T> = T extends any ? FC<T> : never
type Overload = UnionToIntersection<Distributive<TabsProps>>
const Tabs: Overload = (props: TabsProps) => {
if (hasProperty(props, 'withRouter')) {
return <TabsWithRouter {...props} />;
}
return <TabsWithState {...props} />;
};
const Test = () => {
return (
<div>
<Tabs // With correct state props
tabs={[{ name: "myname", active: true }]}
/>
<Tabs // With incorrect state props
baseUrl="something"
tabs={[{ name: "myname", active: true }]}
/>
<Tabs // WIth correct router props
withRouter
tabs={[{ name: "myname", path: "somepath" }]}
/>
<Tabs // WIth correct router props
withRouter
baseUrl="someurl"
tabs={[{ name: "myname", path: "somepath" }]}
/>
<Tabs // WIth incorrect router props
withRouter
tabs={[{ name: "myname", active: true }]}
/>
</div>
);
};
다음과 같은 방법을 사용할 수도 있습니다.
type Overloading =
& ((props: WithStateProps) => JSX.Element)
& ((props: WithRouterProps) => JSX.Element)
이것은 스타일의 문제다.
나는 네가 아직 피곤하지 않기를 바란다.
제3부분dogName
개의 구성 요소가 다음 구속을 갖는다고 가정합니다.
interface ItemWithState {
name: string
active: boolean;
}
interface WithStateProps {
tabs: ItemWithState[];
};
interface ItemWithState {
name: string;
active: boolean;
}
interface ItemWithRouter {
name: string;
path: string;
}
type WithStateProps = {
tabs: ItemWithState[];
};
type WithRouterProps = {
withRouter: true;
baseUrl?: string;
tabs: ItemWithRouter[];
};
const TabsWithRouter: FC<WithRouterProps> = (props) => null
const TabsWithState: FC<WithStateProps> = (props) => null
type TabsProps = WithStateProps | WithRouterProps;
const Tabs = (props: TabsProps) => {
if (props.withRouter) { // error
return <TabsWithRouter {...props} />; // error
}
return <TabsWithState {...props} />; // error
};
import React, { FC } from 'react'
interface ItemWithState {
name: string;
active: boolean;
}
interface ItemWithRouter {
name: string;
path: string;
}
type WithStateProps = {
withRouter?: never;
tabs: ItemWithState[];
};
type WithRouterProps = {
withRouter: true;
baseUrl?: string;
tabs: ItemWithRouter[];
};
const TabsWithRouter: FC<WithRouterProps> = (props) => null
const TabsWithState: FC<WithStateProps> = (props) => null
type TabsProps = WithStateProps | WithRouterProps;
const Tabs = (props: TabsProps) => {
if (props.withRouter) {
return <TabsWithRouter {...props} />;
}
return <TabsWithState {...props} />;
};
const Test = () => {
return (
<div>
<Tabs // With incorrect state props
baseUrl="something"
tabs={[{ name: "myname", active: true }]}
/>
</div>
);
};
interface ItemWithState {
name: string;
active: boolean;
}
interface ItemWithRouter {
name: string;
path: string;
}
type WithStateProps = {
tabs: ItemWithState[];
};
type WithRouterProps = {
withRouter: true;
baseUrl?: string;
tabs: ItemWithRouter[];
};
const TabsWithRouter: FC<WithRouterProps> = (props) => null
const TabsWithState: FC<WithStateProps> = (props) => null
type TabsProps = WithStateProps | WithRouterProps;
const hasProperty = <Obj, Prop extends string>(obj: Obj, prop: Prop)
: obj is Obj & Record<Prop, unknown> =>
Object.prototype.hasOwnProperty.call(obj, prop);
const Tabs = (props: TabsProps) => {
if (hasProperty(props, 'withRouter')) {
return <TabsWithRouter {...props} />;
}
return <TabsWithState {...props} />;
};
const Test = () => {
return (
<div>
<Tabs // With incorrect state props
baseUrl="something"
tabs={[{ name: "myname", active: true }]}
/>
</div>
);
};
// type Overload = FC<WithStateProps> & FC<WithRouterProps>
const Tabs: FC<WithStateProps> & FC<WithRouterProps> = (props: TabsProps) => {
if (hasProperty(props, 'withRouter')) {
return <TabsWithRouter {...props} />;
}
return <TabsWithState {...props} />;
};
const Test = () => {
return (
<div>
<Tabs // With correct state props
tabs={[{ name: "myname", active: true }]}
/>
<Tabs // With incorrect state props
baseUrl="something"
tabs={[{ name: "myname", active: true }]}
/>
<Tabs // WIth correct router props
withRouter
tabs={[{ name: "myname", path: "somepath" }]}
/>
<Tabs // WIth correct router props
withRouter
baseUrl="someurl"
tabs={[{ name: "myname", path: "somepath" }]}
/>
<Tabs // WIth incorrect router props
withRouter
tabs={[{ name: "myname", active: true }]}
/>
</div>
);
};
import React, { FC } from 'react'
interface ItemWithState {
name: string;
active: boolean;
}
interface ItemWithRouter {
name: string;
path: string;
}
type WithStateProps = {
tabs: ItemWithState[];
};
type WithRouterProps = {
withRouter: true;
baseUrl?: string;
tabs: ItemWithRouter[];
};
const TabsWithRouter: FC<WithRouterProps> = (props) => null
const TabsWithState: FC<WithStateProps> = (props) => null
type TabsProps = WithStateProps | WithRouterProps;
const hasProperty = <Obj, Prop extends string>(obj: Obj, prop: Prop)
: obj is Obj & Record<Prop, unknown> =>
Object.prototype.hasOwnProperty.call(obj, prop);
// credits goes to https://stackoverflow.com/a/50375286
type UnionToIntersection<U> = (U extends any ? (k: U) => void : never) extends (
k: infer I
) => void
? I
: never;
type Distributive<T> = T extends any ? FC<T> : never
type Overload = UnionToIntersection<Distributive<TabsProps>>
const Tabs: Overload = (props: TabsProps) => {
if (hasProperty(props, 'withRouter')) {
return <TabsWithRouter {...props} />;
}
return <TabsWithState {...props} />;
};
const Test = () => {
return (
<div>
<Tabs // With correct state props
tabs={[{ name: "myname", active: true }]}
/>
<Tabs // With incorrect state props
baseUrl="something"
tabs={[{ name: "myname", active: true }]}
/>
<Tabs // WIth correct router props
withRouter
tabs={[{ name: "myname", path: "somepath" }]}
/>
<Tabs // WIth correct router props
withRouter
baseUrl="someurl"
tabs={[{ name: "myname", path: "somepath" }]}
/>
<Tabs // WIth incorrect router props
withRouter
tabs={[{ name: "myname", active: true }]}
/>
</div>
);
};
type Overloading =
& ((props: WithStateProps) => JSX.Element)
& ((props: WithRouterProps) => JSX.Element)
dogName
개의 구성 요소가 다음 구속을 갖는다고 가정합니다.canBark
이 빈 문자열이거나 설정되지 않은 경우 dogName
은 canBark
은 True
type NonEmptyString<T extends string> = T extends '' ? never : T;
type WithName = {
dogName: string,
canBark: true,
}
type WithoutName = {
dogName?: '',
canBark: false
};
type Props = WithName | WithoutName;
React 구성 요소는 일반 함수일 뿐이므로 일반 매개변수를 사용하여 다시 로드할 수 있습니다.
type Overloadings =
& ((arg: { canBark: false }) => JSX.Element)
& ((arg: { dogName: '', canBark: false }) => JSX.Element)
& (<S extends string>(arg: { dogName: NonEmptyString<S>, canBark: true }) => JSX.Element)
const Animal: Overloadings = (props: Props) => {
return null as any
}
테스트해 보겠습니다.import React, { FC } from 'react'
type NonEmptyString<T extends string> = T extends '' ? never : T;
type WithName = {
dogName: string,
canBark: true,
}
type WithoutName = {
dogName?: '',
canBark: false
};
type Props = WithName | WithoutName;
type Overloadings =
& ((arg: { canBark: false }) => JSX.Element)
& ((arg: { dogName: '', canBark: false }) => JSX.Element)
& (<S extends string>(arg: { dogName: NonEmptyString<S>, canBark: true }) => JSX.Element)
const Animal: Overloadings = (props: Props) => {
return null as any
}
const Test = () => {
return (
<>
<Animal dogName='' canBark={false} /> // ok
<Animal dogName='a' canBark={true} /> // ok
<Animal canBark={false} /> // ok
<Animal dogName='a' canBark={false} /> // error
<Animal dogName='' canBark={true} /> // error
<Animal canBark={true} /> // error
</>
)
}
제4부분
만약 우리가 하나의 구성 요소를 가지고 있다면, 그것은 foo
과 bar
의 속성이 문자열이길 희망하지만, 속성 foo
은 hello
이 될 수 없다.
이를 위해 우리는 foo
과 bar
속성에 대해 현식 범형을 사용해야 한다.
이것은 매우 간단하다.
import React from 'react'
type Props<F extends string = '', B extends string = ''> = {
foo: F;
bar: B;
}
type ConditionalProps<T> = T extends { foo: infer Foo; bar: string } ? Foo extends 'hello' ? never : T : never
const Example = <F extends string, B extends string>(props: ConditionalProps<Props<F, B>>) => {
return null as any
}
const Test = () => {
<>
<Example foo='hello' bar='bye' /> // expected error
<Example foo='not hello' bar='1' /> // ok
</>
}
읽어주셔서 감사합니다.
Reference
이 문제에 관하여(TypeScript React props에 React props를 전문적으로 입력하는 방법 - 노조 유형 구분과 투쟁), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다
https://dev.to/captainyossarian/how-to-type-react-props-as-a-pro-2df2
텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
우수한 개발자 콘텐츠 발견에 전념
(Collection and Share based on the CC Protocol.)
import React from 'react'
type Props<F extends string = '', B extends string = ''> = {
foo: F;
bar: B;
}
type ConditionalProps<T> = T extends { foo: infer Foo; bar: string } ? Foo extends 'hello' ? never : T : never
const Example = <F extends string, B extends string>(props: ConditionalProps<Props<F, B>>) => {
return null as any
}
const Test = () => {
<>
<Example foo='hello' bar='bye' /> // expected error
<Example foo='not hello' bar='1' /> // ok
</>
}
Reference
이 문제에 관하여(TypeScript React props에 React props를 전문적으로 입력하는 방법 - 노조 유형 구분과 투쟁), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://dev.to/captainyossarian/how-to-type-react-props-as-a-pro-2df2텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)