【问题标题】:Typescript: object type to array type (tuple)打字稿:对象类型到数组类型(元组)
【发布时间】:2019-03-22 03:34:07
【问题描述】:

我有这个:

interface Obj {
    foo: string,
    bar: number,
    baz: boolean
}

所需的类型是这个元组:

[string, number, boolean]

如何将接口转换为元组?

更新:

我原来的问题是: 我制作了一些具有声明性精神的固执己见的库,用户应该在对象文字中描述函数的参数。像这样:

let paramsDeclaration = {
  param1: {
    value: REQUIRED<string>(),
    shape: (v) => typeof v === 'string' && v.length < 10
  },
  param2: {
    value: OPTIONAL<number>(),
    ...
  },
}

然后库获取这个对象并创建一个带有参数的函数:

   (param1: string, param2?: number) => ...

所以,制作这样的功能不是问题,问题是正确键入它,以便用户获得良好的代码完成(IntelliSense)。

附:我知道这无法解决,但知道什么是最接近的解决方法/hack 会很有趣。

【问题讨论】:

  • 订单对您来说很重要吗?我认为这是不可能的。
  • 您好!秩序并不重要,但最好保持秩序。如果你说不可能,那肯定是不可能的。伤心:-(
  • 你能多谈谈你原来的问题吗?我们或许能找到另一种解决方案。
  • 当你说“转换”时,你的意思是你想从一个匹配接口的对象创建一个匹配元组类型的数组吗?还是您的意思是要将 Obj 接口更改为元组类型?
  • 我希望@MattMcCutchen 仍然愿意提供帮助。我已将原始问题添加到答案中。

标签: typescript tuples


【解决方案1】:

并不是这个问题的真正答案,但由于我实际上认为它不可能做到,希望这至少在某些方面有所帮助:

function REQUIRED<T>(): T {
    //...
}
function OPTIONAL<T>(): T {
    //...
}

interface ParamsDeclaration {
    readonly [paramName: string]: {
        readonly value: any;
        readonly shape?: Function;
    };
}

type Func<T> = T extends {
    readonly [paramName: string]: {
        readonly value: infer U;
    };
} ? (...params: Array<U>) => void
    : never;

function create<T extends ParamsDeclaration>(paramsDeclaration: T): Func<T> {

    // ...
}

const paramsDeclaration = {
    param1: {
        value: REQUIRED<string>(),
        shape: (v: any) => typeof v === 'string' && v.length < 10
    },
    param2: {
        value: OPTIONAL<number>(),
        //...
    },
};
// Type is '(...params: (string | number)[]) => void'
const func1 = create(paramsDeclaration);
func1('1', 2); // Ok
func1(2, '1'); // Ok, but I assume not what you want
func1(Symbol()); // TS error

【讨论】:

  • 感谢您的宝贵时间!发现你的方法很有趣。是的,不幸的是顺序很重要,但似乎无法以某种方式推断接口/对象的属性顺序。
【解决方案2】:

其他建议,
需要设置参数的顺序。

interface Param {
    readonly value: any;
    readonly shape?: Function;
}
type Func<T extends Record<string, Param>, orders extends (keyof T)[]> = (...args:{
    [key in keyof orders]:orders[key] extends keyof T ? T[orders[key]]['value']: orders[key];
})=>void;

function create<T extends Record<string, Param>, ORDERS extends (keyof T)[]>(params: T, ...orders:ORDERS): Func<T, ORDERS> {
    return 0 as any;
}

const func1 = create({a:{value:0}, b:{value:''}, c:{value:true}}, 'a', 'b', 'c');
func1(0, '1', true); // ok
func1(true, 0, '1'); // error


带数组的参数声明

type Func2<T extends Param[]> = (...args:{
    [key in keyof T]:T[key] extends Param ? T[key]['value'] : T[key]
})=>void;

function create2<T extends Param[], ORDERS extends (keyof T)[]>(...params: T): Func2<T> {
    return 0 as any;
}

const func2 = create2({value:0}, {value:''}, {value:true});
func2(0, '1', true); // ok
func2(true, 0, '1'); // error

【讨论】:

    【解决方案3】:

    90% 的时间你认为在 Typescript 中某事是不可能的,真正的答案是它可能的,但你可能不应该这样做。

    这是使用this answer 中的TuplifyUnion 的解决方案,它将联合类型转换为元组类型;请注意,我们需要从对象键的联合开始,而不是它的值,因为值本身可能是联合(例如,boolean 在技术上是 true | false)。

    阅读链接的答案以详细说明 // oh boy don't do this 评论的含义。如果您希望 API 的用户指定 API 生成的函数的参数,那么明智的选择是首先在数组中接受这些参数说明。

    type ObjValueTuple<T, KS extends any[] = TuplifyUnion<keyof T>, R extends any[] = []> =
      KS extends [infer K, ...infer KT]
      ? ObjValueTuple<T, KT, [...R, T[K & keyof T]]>
      : R
    
    // type Test = [string, number, boolean]
    type Test = ObjValueTuple<Obj>
    

    Playground Link

    【讨论】:

      猜你喜欢
      • 2023-03-15
      • 1970-01-01
      • 1970-01-01
      • 2020-06-15
      • 2022-01-23
      • 2023-04-01
      • 2017-02-20
      • 1970-01-01
      • 2022-08-22
      相关资源
      最近更新 更多