如何将类型定义为定义为 [key: string] key/value 结构的对象键列表 - TypeScript

How to define type as a list of object keys defined like [key: string] key/value structure - TypeScript

我想像这样在 TypeScript 中定义接口,但我不知道该怎么做:

export interface SomeInterface {
  testProp:{
  [key: string]: {
    prop1: string;
    prop2?: string;
    prop3?: string;
    ....

  };
}
  requiredProps: ???? // <- here i would like to define type to be array of used keys
}

e.x。对象:

 const value = {
    testProp: {
      orange: {
        prop1: 'test1',
      },
      kiwi: {
        prop1: 'random text',
      },
      lemon: {
        prop1: 'text',
      },
    },
   requiredProps: [] // posible items in array "lemon", "kiwi", "orange"
  };

我尝试将 requiredProps 定义为 requiredProps: [keyof Pick<SomeInterface,"testProp"] 但没有成功

没有与您尝试执行的操作相对应的特定类型。但是,您可以将其表示为 generic 类型。例如:

export interface SomeInterface<K extends PropertyKey, R extends K> {
    testProp: Record<K, {
        prop1: string;
        prop2?: string;
        prop3?: string;
    }>;
    requiredProps: R[]
}

这将按照您想要的方式进行约束,但是为了生成 SomeInterface 类型的值,您需要指定 KR 参数。您可以让编译器推断那些具有辅助函数的函数:

const asSomeInterface = <K extends PropertyKey, R extends K>(x: SomeInterface<K, R>) => x;

并像这样使用它:

const value = asSomeInterface({
    testProp: {
        orange: {
            prop1: 'test1',
        },
        kiwi: {
            prop1: 'random text',
        },
        lemon: {
            prop1: 'text',
        },
    },
    requiredProps: ["orange", "kiwi"]
});

当您向 requiredProps 添加一个不是 testProp 的键的元素时,您会看到所需的错误:

asSomeInterface({
    testProp: {
        a: { prop1: "" }, b: { prop1: "" }, c: { prop1: "" }
    },
    requiredProps: ["a", "b", "c", "d"] // error!
    // --------------------------> ~~~~
    // Type '"d"' is not assignable to type '"a" | "b" | "c"'
})

使 SomeInterface<K, R> 成为通用类型更为复杂,因为处理它们的任何值或函数都需要携带额外的类型参数。您可能会考虑仅对公开给输入不保证正确的外部用户的代码使用泛型,然后在内部将类型扩展为安全性较低但更容易传递的非泛型版本:

// code seen by outside users, enforces constraint
function externalFunction<K extends string, R extends K>(
  someInterface: SomeInterface<K, R>
) {
    internalFunction(someInterface)
}

// code not exposed outside, widens to non-generic version
type SomeWiderInterface = SomeInterface<string, string>
const someWiderValue: SomeWiderInterface = value; // accepted
function internalFunction(someWiderInterface: SomeWiderInterface) {
    // do stuff
}

Playground link to code