programing

typescript 인터페이스에는 두 가지 속성 중 하나가 필요합니다.

stoneblock 2023. 4. 4. 20:52

typescript 인터페이스에는 두 가지 속성 중 하나가 필요합니다.

이 인터페이스를 만들고 있습니다.

export interface MenuItem {
  title: string;
  component?: any;
  click?: any;
  icon: string;
}
  1. component ★★★★★★★★★★★★★★★★★」click
  2. 두 속성을 모두 설정할 수 없도록 요구하는 방법이 있습니까?

「 」의 ExcludeTypeScript 2.8에서 추가된 타입은 다음 중 하나 이상의 속성을 필요로 하는 일반화된 방법을 제공합니다.

type RequireAtLeastOne<T, Keys extends keyof T = keyof T> =
    Pick<T, Exclude<keyof T, Keys>> 
    & {
        [K in Keys]-?: Required<Pick<T, K>> & Partial<Pick<T, Exclude<Keys, K>>>
    }[Keys]

또한 하나만을 제공하도록 요구하는 부분적이지만 절대적인 방법은 다음과 같습니다.

type RequireOnlyOne<T, Keys extends keyof T = keyof T> =
    Pick<T, Exclude<keyof T, Keys>>
    & {
        [K in Keys]-?:
            Required<Pick<T, K>>
            & Partial<Record<Exclude<Keys, K>, undefined>>
    }[Keys]

다음은 TypeScript playground 링크입니다.이 링크에서는 양쪽이 동작하고 있습니다.

「 」의 .RequireOnlyOneTypeScript가 컴파일 시 런타임에 존재하는 모든 속성을 항상 알 수 있는 것은 아닙니다. So obviously 그러니 당연하게도RequireOnlyOne추가 속성을 방지하기 위해 아무것도 할 수 없습니다.알지 못하는 추가 속성을 막기 위해 아무것도 할 수 없습니다. I provided an example of how 제가 예를 들어 설명 드렸는데요.RequireOnlyOne놀이터 링크의 끝에 놓으면 할 수 있다.놀이터 링크 끝에 있는 것을 놓칠 수 있습니다.

다음 예를 사용하여 동작의 개요를 나타냅니다.

interface MenuItem {
  title: string;
  component?: number;
  click?: number;
  icon: string;
}

type ClickOrComponent = RequireAtLeastOne<MenuItem, 'click' | 'component'>
  1. Pick<T, Exclude<keyof T, Keys>>부에서RequireAtLeastOne다가 되다{ title: string, icon: string}, 함 포 되 지 은 경 의 성 은 되 which속 not않다니입 in변 proper지 included키ties에않 of are the keys the unchanged which not ..'click' | 'component'

  2. { [K in Keys]-?: Required<Pick<T, K>> & Partial<Pick<T, Exclude<Keys, K>>> }[Keys]부에서RequireAtLeastOne다가 되다

    { 
        component: Required<{ component?: number }> & { click?: number }, 
        click: Required<{ click?: number }> & { component?: number } 
    }[Keys]
    

    어느 쪽이 되는가

    {
        component: { component: number, click?: number },
        click: { click: number, component?: number }
    }['component' | 'click']
    

    결국엔 그게

    {component: number, click?: number} | {click: number, component?: number}
    
  3. 위의 1단계와 2단계의 교차점

    { title: string, icon: string} 
    & 
    ({component: number, click?: number} | {click: number, component?: number})
    

    으로 심플화

    { title: string, icon: string, component: number, click?: number} 
    | { title: string, icon: string, click: number, component?: number}
    

단일 인터페이스에서는 사용할 수 없습니다.타입에는 조건부 로직이 없고 서로 의존할 수 없기 때문에 인터페이스를 분할하여 다음과 같이 할 수 있습니다.

export interface BaseMenuItem {
  title: string;
  icon: string;
}

export interface ComponentMenuItem extends BaseMenuItem {
  component: any;
}

export interface ClickMenuItem extends BaseMenuItem {
    click: any;
}

export type MenuItem = ComponentMenuItem | ClickMenuItem;

더 간단한 해결책이 있습니다.다음과 같은 복잡한 조건 유형을 사용할 필요가 없습니다.

  1. 컴포넌트 또는 클릭을 설정하도록 요구하는 방법이 있습니까? (포함)
type MenuItemOr = {
    title: string;
    icon: string;
} & ({ component: object } | { click: boolean }) 
// brackets are important here: "&" has precedence over "|"

let testOr: MenuItemOr;
testOr = { title: "t", icon: "i" } // error, none are set
testOr = { title: "t", icon: "i", component: {} } // ✔
testOr = { title: "t", icon: "i", click: true } // ✔
testOr = { title: "t", icon: "i", click: true, component: {} } // ✔

유니언 타입(|) corresponds to inclusive )는 포함에 대응합니다.OR. 이것은 무조건적인 속성과 교차합니다.

연산자를 사용하여 값을 다음 구성 요소 중 하나로 다시 좁힙니다.

if ("click" in testOr) testOr.click // works 
  1. 두 속성을 모두 설정할 수 없도록 요구하는 방법이 있습니까? (Exclusive / )
type MenuItemXor = {
    title: string;
    icon: string;
} & (
        | { component: object; click?: never }
        | { component?: never; click: boolean }
    )

let testXor: MenuItemXor;
testXor = { title: "t", icon: "i" } // error, none are set
testXor = { title: "t", icon: "i", component: {} } // ✔
testXor = { title: "t", icon: "i", click: true } // ✔
testXor = { title: "t", icon: "i", click: true, component: {} } //error,both set

기본적으로 어느 쪽인가 하면 component or or or openicle. click설정할 수 있습니다.다른 하나를 동시에 추가해서는 안 됩니다.TS는 다음과 같은 식별된 유니언 유형을 만들 수 있습니다.MenuItemXor 경우, 이 경우, 이 말은 에 해당됩니다.XOR.

★★★★★★★★★★★★★★★★★.XOR입니다.MenuItemXor수락된 답변으로는 불가능합니다.


놀이터.

1 말하면, 「 」입니다.prop?: neverprop?: undefined하지만 전자는 종종 일러스트레이션에 사용됩니다.

인터페이스가 여러 개 없는 대체 방법은

export type MenuItem = {
  title: string;
  component: any;
  icon: string;
} | {
  title: string;
  click: any;
  icon: string;
};

const item: MenuItem[] = [
  { title: "", icon: "", component: {} },
  { title: "", icon: "", click: "" },
  // Shouldn't this error out because it's passing a property that is not defined
  { title: "", icon: "", click: "", component: {} },
  // Does error out :)
  { title: "", icon: "" }
];

단일 속성을 설정해야 하는 부분 유사 생성 방법에서 유사한 질문을 했습니다.

위의 내용은 단순화할 수 있지만 읽기 쉬울 수도 있고 그렇지 않을 수도 있습니다.

export type MenuItem = {
  title: string;
  icon: string;
} & (
 {component: any} | {click: string}
)

TypeScript 에서는 AND/OR 를 사용하는 오브젝트에 대한 추가 속성을 사용할 수 있기 때문에 둘 다 추가할 수 없습니다.https://github.com/Microsoft/TypeScript/issues/15447 를 참조해 주세요.

나는 이것을 사용한다.

type RequireField<T, K extends keyof T> = T & Required<Pick<T, K>>

사용방법:

let a : RequireField<TypeA, "fieldA" | "fieldB">;

이렇게 하면fieldA 그리고. fieldB필수의.

나는 결국 다음과 같이 되었다.

export interface MenuItem {
  title: string;
  icon: string;
}

export interface MenuItemComponent extends MenuItem{
  component: any;
}

export interface MenuItemClick extends MenuItem{
  click: any;
}

그 후 나는 다음과 같이 했습니다.

 appMenuItems: Array<MenuItemComponent|MenuItemClick>;

하지만 단일 인터페이스로 모델을 만들 수 있는 방법이 있길 바랐습니다.

사용하는 것을 좋아합니다.Pick이러한 종류의 조건부 요건을 설정하기 위한 모든 속성을 포함하는 기본 유형과 함께 사용합니다.

interface MenuItemProps {
  title: string;
  component: any;
  click: any;
  icon: string;
}

export interface MenuItem =
  Pick<MenuItemProps, "title" | "icon" | "component"> |
  Pick<MenuItemProps, "title" | "icon" | "click">

이것은 깨끗하고 유연합니다.선언을 단순하고 읽기 쉬운 상태로 유지하면서 "모든 속성, 이 두 속성 또는 이 하나의 속성 중 하나를 필요로 한다"와 같은 것들을 주장하면서 요구 사항을 임의로 복잡하게 만들 수 있습니다.

다음은 둘 다 구현하기 위한 간단한 방법입니다.

type MenuItem =  {
  title: string;
  component: any;
  click?: never;
  icon: string;
} | {
  title: string;
  component?: never;
  click: any;
  icon: string;
}

// good
const menuItemWithComponent: MenuItem = {
  title: 'title',
  component: "my component",
  icon: "icon"
}

// good
const menuItemWithClick: MenuItem = {
  title: 'title',
  click: "my click",
  icon: "icon"
}

// compile error
const menuItemWithBoth: MenuItem = {
  title: 'title',
  click: "my click",
  component: "my click",
  icon: "icon"
}

또 다른 솔루션:

type RequiredKeys<T, K extends keyof T> = Required<Pick<T, K>> & Omit<T, K>;

type MenuItem2 = RequiredKeys<MenuItem, "component" | "click">;

이 어프로치에서는,never그리고.Omit. 여기서 얻을 수 있는 이점은 알기 쉽고 속성을 추가해야 할 경우에도 쉽게 업데이트할 수 있다는 것입니다.

interface Base {
  title: string;
  icon: string;
  component?: never;
  click?: never;
}

interface OnlyComponent {
  component: any;
}

interface OnlyClick {
  click: any;
}

export type MenuItem = (Omit<Base, 'component'> & OnlyComponent) | (Omit<Base, 'click'> & OnlyClick);

사용할 수 있습니다.in사례를 좁히다MenuItem:

const item: MenuItem = {
  title: 'A good title';
  icon: 'fa-plus';
  component: SomeComponent;
};

//...

if('component' in item) {
  const Comp = item.component;
  //...
}

위의 멋진 답변만 더하면 됩니다!또한 필요한 기능을 갖춘 Partial 버전을 찾는 동안 이곳에 도착한 사람들을 위해!여기 내가 찍으려고 만든 조각이 있어!

부분 요구

인터페이스의 일부를 사용하는 경우, 그 사이에 일부 필드가 필요합니다.어떻게 하면 좋을까

export type PartialReq<T, Keys extends keyof T = keyof T> =
    Pick<Partial<T>, Exclude<keyof T, Keys>>
    & {
        [K in Keys]: T[K]
    };

예제를 사용하다

export interface CacheObj<SigType = any, ValType = any> {
    cache: Map<SigType, ValType>,
    insertionCallback: InsertionCallback<SigType, ValType> // I want this to be required
}

// ...

export class OneFlexibleCache<SigType = any, ValType = any> {
    private _cacheObj: CacheObj<SigType, ValType>;

    constructor(
        cacheObj: PartialReq<CacheObj<SigType, ValType>, 'insertionCallback'> // <-- here
                                                                           //  i used it
    ) {
        cacheObj = cacheObj || {};

        this._cacheObj = {

// ...

// _______________ usage
this._caches.set(
    cacheSignature,
    new OneFlexibleCache<InsertionSigType, InsertionValType>({
        insertionCallback // required need to be provided
    })
);

여기 보면 완벽하게 동작하는 것을 알 수 있습니다.

여기에 이미지 설명 입력

필요한 정보가 제공되지 않은 경우

여기에 이미지 설명 입력

업데이트: 위에서 설명한 사용법에 대해 더 나은 답변을 제시합니다.

의사한테 가보니 오미트가 있더군요

https://www.typescriptlang.org/docs/handbook/utility-types.html#omittk

추가하러 왔어요.그런데 그 전에 이 멋진 대답을 봤어요.모두 대상:

https://stackoverflow.com/a/48216010/7668448

이것 좀 봐!모든 다양한 버전의 Typescript에 대해 이 작업을 수행하는 방법을 보여 줍니다.그리고 반복하지 않기 위해서! 가서 확인해봐!

언급URL : https://stackoverflow.com/questions/40510611/typescript-interface-require-one-of-two-properties-to-exist