Typescript:如何键入一个键为数组索引的泛型对象?

mmvthczy  于 2023-03-19  发布在  TypeScript
关注(0)|答案(2)|浏览(139)

我有一个泛型数组,其中每个值都可以是未定义的。所以如果我有一个对象,在某些情况下索引作为键,那么使用它会更好。

// Simple Example
// bad
exampleFunc<[string, string, string, number, date]>([undefined, undefined, undefined, 1, undefined])
// good
exampleFunc<[string, string, string, number, date]>({"3":  1})

我当前的打字方式:

export type DataTypes = string | number | Date;
type ColumnConfigShort<T extends DataTypes> = Partial<ColumnConfig<T>>;
type ToColumnConfigShort<T> = T extends DataTypes
  ? T extends string
    ? ColumnConfigShort<string>
    : T extends number
    ? ColumnConfigShort<number>
    : T extends Date
    ? ColumnConfigShort<Date>
    : never
  : never;

type ColumnsConfigShort<T extends DataTypes[]> = Partial<{
  [I in keyof T]: ToColumnConfigShort<T[I]>;
}>;

type howItCanBeUsed = ColumnsConfigShort<[number, number, string]>;
// howItCanBeUsed = [
//  (Partial<ColumnConfig<number>> | undefined)?,
//  (Partial<ColumnConfig<number>> | undefined)?,
//  (Partial<ColumnConfig<string>> | undefined)?,
// ]
//
// But i need this type:
// dream = {
//   "0"?: Partial<ColumnConfig<number>>;
//   "1"?: Partial<ColumnConfig<number>>;
//   "2"?: Partial<ColumnConfig<string>>;
// }
ruarlubt

ruarlubt1#

您需要的是Record〈Keys,Type〉类型,它表示一个对象,该对象的键类型为Keys,值类型为Type
例如,

const arrayLikeObject:Record<string, number>  = {"3": 1};

虽然这并不是完全类型安全的,因为它也允许键本质上不是 * 数字作为字符串 *,我建议保持键为数字而不是字符串。这样,对象的类型将是更强类型化的。

const arrayLikeObject:Record<number, number>  = {3: 1};
7kjnsjlb

7kjnsjlb2#

看起来你并不想禁止某人传递像[undefined, undefined, undefined, 1, undefined]这样的数组,而是想支持传递像{ "3": 1 }这样的普通对象。如果是这样,那么我们应该写一个实用类型,把元组类型转换成一个普通对象类型,而不需要任何原型数组属性:

type TupleToPlainObj<T> = { [I in keyof T & `${number}`]: T[I] }

type BaseTuple = [string, string, string, number, Date];
type X = TupleToPlainObj<BaseTuple>;
/* type X = {
    0: string;
    1: string;
    2: string;
    3: number;
    4: Date;
} */

这是有效的,因为我们是mapping,元组的键是 *numeric string *,这是template literal type${number}所表示的(它是一个“pattern”模板文本类型,如microsoft/TypeScript#40598所描述的)。
在您的用例中,您希望所有这些都是可选的,因此我们可以使用Partial<T>实用程序类型:

type Y = Partial<X>
/* type Y = {
    0?: string | undefined;
    1?: string | undefined;
    2?: string | undefined;
    3?: number | undefined;
    4?: Date | undefined;
} */

这意味着您的exampleFunc()调用签名可能如下所示:

declare function exampleFunc<T extends any[]>(arg: Partial<TupleToPlainObj<T>>): void;

这将提供所需的行为:

exampleFunc<BaseTuple>([undefined, undefined, undefined, 1, undefined]); // okay
exampleFunc<BaseTuple>({ "3": 1 }); // okay
exampleFunc<BaseTuple>({ "2": true }); // error, boolean isn't string
exampleFunc<BaseTuple>({ "5": "oops" }); // error, extra property

Playground代码链接

相关问题