代码之家  ›  专栏  ›  技术社区  ›  0xts

在typescript中设置接口属性的动态值或条件值

  •  0
  • 0xts  · 技术社区  · 4 年前

    我正在尝试构建一个显示堆栈图像的react组件,类似于 this , 为此,我试图用接口和类型来强制它以特定的方式使用一些道具。以下是我以前的接口代码:

    旧接口.ts

    export interface ImageStackProps{
      images: string[];
      backgroundColor: string;
      extraBannerBackgroundColor: string;
      imageSize: ImageSize<string>;
      imagesToShow: number;
      direction?: StackDirection<"column" | "row" | "column-reverse" | "row-reverse">;
    }
    
    type StackDirection<T> = T | Array<T | null> | { [key: string]: T | null };
    type ImageSize<T> = { width: T; height: T };
    

    imagesToShow属性决定它将显示多少图像,以及是否显示剩余的额外图像横幅。虽然没有必要,但我试图基于images属性(它获得一个base64编码图像的数组)来约束imagesToShow属性,这样就永远不允许大于数组最后一个索引的值。因此,为此,我修改了我的界面,使其看起来像这样:

    新接口.ts

    export interface ImageStackProps<ImagesArray extends Array<string>> {
      images: ImagesArray;
      backgroundColor: string;
      extraBannerBackgroundColor: string;
      imageSize: ImageSize<string>;
      imagesToShow: AllowedArrayIndexes<ImagesArray>;
      direction?: StackDirection<"column" | "row" | "column-reverse" | "row-reverse">;
    }
    
    type StackDirection<T> = T | Array<T | null> | { [key: string]: T | null };
    type ImageSize<T> = { width: T; height: T };
    
    type ArrayKeys<T extends Array<string>> = keyof T;
    type AllowedArrayIndexes<T extends Array<string>> = Exclude<ArrayKeys<T>, ArrayKeys<string[]>>;
    

    但在这一点上,我面临的问题是 ImagesStack 组件,我需要将类型传递给 ImageStackProps ,这是我 图像堆栈 组件接收 props.images

    我理解并知道没有必要有这个约束,没有它也可以很好地工作,但我正在看看我是否能做到这一点。我确信我没有正确地处理这个问题。有更好的方法来执行这一点吗?或者更好的是,这在某种程度上是可以实现的吗?我应该如何修改我的界面?

    0 回复  |  直到 4 年前
        1
  •  1
  •   Alex Wayne    4 年前

    但目前我面临的问题是,要键入我的ImagesStack组件,我需要将一个类型传递给ImageStackProps,这是我的images Stack组件在其props.images中接收的内容。

    这是容易的部分。您只需将组件设为泛型,然后将该泛型类型参数传递给props类型。

    function MyComponent<T extends readonly string[]>(props: ImageStackProps<T>) {
      return <></>
    }
    

    但您的解决方案的其余部分存在一些问题。

    • AllowedArrayIndexes 返回字符串键( "0" | "1" | "2" ),而不是数字( 0 | 1 | 2 )。
    • AllowedArrayIndex 返回索引,比应该允许的少一个。3项数组具有索引 0|1|2 但是应该允许您显示3个图像。

    我提出了一种不同的方法。


    首先,我们需要获得 number 元组中的索引。 This issue 有一些巧妙的方法。但让我们继续这样做:

    type AllowedCounts<T extends { length: number }> = Partial<T>["length"];
    

    这看起来像是伏都教,但让我们走过来。

    元组具有已知的长度。 [number,number,number] 有一个 length 属于 3 但是 Partial<T> 使所有成员都是可选的,因此:

    Partial<[number, number, number]>
    // yields
    [number?, number?, number?]
    

    这意味着元组可能缺少成员,因此长度会更小。以下所有类型都可分配给 [number?, number?, number?] :

    []                       // length 0
    [number]                 // length 1
    [number, number]         // length 2
    [number, number, number] // length 3
    

    因此:

    Partial<[number, number, number]>['length'] // 0 | 1 | 2 | 3
    

    你甚至可以排除 0 如果你愿意的话,因为显示一个没有图像的图像堆栈没有多大意义。

    Exclude<Partial<[number, number, number]>['length'], 0> // 1 | 2 3
    

    这同样适用于无边界数组类型,因为:

    type A = string[]['length'] // number
    type B = Partial<string[]>['length'] // number
    

    尽管你 不能 排除 0 在这种情况下,由于typescript没有负类型:

    Exclude<number, 0> // number, sadly.
    

    现在使用它很简单:

    export interface ImageStackProps<ImagesArray extends readonly string[]> {
      images: ImagesArray;
      imagesToShow: AllowedCounts<ImagesArray>;
    }
    
    type AllowedCounts<T extends { length: number }> = Partial<T>["length"];
    
    // A component
    function MyComponent<T extends readonly string[]>(props: ImageStackProps<T>) {
      return <></>
    }
    
    // With known tuple length at compile time
    const imagesTuple = ['a.jpg', 'b.jpg'] as const // 2 images, component can show 
    const jsxA = <MyComponent images={imagesTuple} imagesToShow={0} />
    const jsxB = <MyComponent images={imagesTuple} imagesToShow={1} />
    const jsxC = <MyComponent images={imagesTuple} imagesToShow={2} />
    const jsxD = <MyComponent images={imagesTuple} imagesToShow={3} /> // error
    const jsxE = <MyComponent images={imagesTuple} imagesToShow={50} /> // error
    
    
    // With unknown array length at compile time
    declare const imagesArr: string[] // from an api, for example.
    const jsxX = <MyComponent images={imagesArr} imagesToShow={1} /> // fine
    const jsxY = <MyComponent images={imagesArr} imagesToShow={123456} /> // fine
    

    Playground