typescript 从动态类型化模型导出关联接口

j2qf4p5b  于 2023-02-05  发布在  TypeScript
关注(0)|答案(2)|浏览(111)

这是这个问题的后续:Best way to get dynamic type definition in typescript from a configuration object

上一个答案的摘要

综上所述,这可以为给定配置对象的模型示例正确地创建动态类型,我们的堆栈目前可以将作为类型不可知的json blob的模型转换为适当的类型,同时还需要静态类型检查,这个解决方案效果很好。
akka

定义动态类型化模型
class Person extends ModelFromAttributes({
  joined: Date,
  name: String,
  first_name: String,
  last_name: String,
  age: Number,
  skills: [String],
  locale: (v: any) => (v === 'vacation' ? null : String),
  pets: [Pet],
}) {}
示例化和智能化结果
// bill's properties and types will be properly intellisensed

// bill.joined; // (property) joined?: Date | undefined
// bill.locale; // (property) locale?: string | null | undefined
// bill.name; // (property) name?: string | undefined
// etc..

const bill = new Person({
  joined: '2021-02-10',
  first_name: 'bill',
  last_name: 'smith',
  name: 'bill smith',
  age: '26',
  skills: ['python', 'dogwalking', 500],
  locale: 'vacation',
  pets: [
    {
      name: 'rufus',
      type: 'dog',
    },
  ],
});
新问题:正在获取其他关联接口?

所以上面的代码很好用,但是我经常会把模型从示例导出到普通对象,然后传递给组件。这是模型的值,但是本质上没有方法,只有原始属性(扁平化)。在这个有效负载之上,可能需要转换成不同的大小写,so im wondering if possible to use similar strategy / pass in the types to the base model class to not have to repeat myself with non-dry interface definitions since came this far already?
这是一个堆栈 lightning 战与以下:https://stackblitz.com/edit/cejkqp?file=index.ts&view=editor

以上示例的附加接口示例

一个二个一个一个

理想的智能

理想情况下,我可以对上面的结果值进行智能感知和类型检查,但类似于原始解决方案的动态检查,同时还可以展平子类型。我知道这可能是非常规的,但我只是想知道是否可以使用一些魔法。

// bill.toFlattenedObject().joined; // (property) joined?: Date | undefined
// bill.toFlattenedObject().age; // (property) number?: Date | undefined
// bill.toFlattenedObject().pets[0]?.name; // (property) name?: String | undefined
qacovj5a

qacovj5a1#

ModelFromAttributes类型声明publicAttributes的方式类似,它可以使用适当的类型声明toFlattenedObject
打字机游戏场
snake_case类型比较困难,TypeScript的模板字符串常量可以做一些字符串操作,基于GitHub的讨论(参见此处开始的评论),可以将camelCase转换为snake_case,但这需要一些泛型技巧(可能会对编译时间产生负面影响)。

kqlmhetl

kqlmhetl2#

所以Josh Kelley的第一个答案肯定适用于扁平化,我也能够弄清楚 shell ,并使2个主要的仿制药更干一点。所以我决定发布这个更新的答案。这似乎是工作:
打字机Playground
主要类型定义修改如下

/*********************************
 * Modified MFA with flat option  *
 *********************************/

type MFA<T, C extends 'flat' | 'normal'= 'normal'> = T extends typeof Date
  ? Date
  : T extends typeof String
  ? string
  : T extends typeof Number
  ? number
  : T extends typeof Boolean
  ? boolean
  : T extends abstract new (...args: any) => infer R
  ? C extends 'flat' ? (R extends { publicAttributes: infer S }
    ? MFA<S, C> 
    : R)
  : R
  : T extends (...args: any) => infer R
  ? MFA<R, C>
  : T extends object
  ? { [K in keyof T]: MFA<T[K], C> }
  : T;

/*********************************
 * Casing convert Generics (deep) *
 *********************************/

type CamelCase<S extends string> =
  S extends `${infer P1}_${infer P2}${infer P3}`
    ? `${Lowercase<P1>}${Uppercase<P2>}${CamelCase<P3>}`
    : Lowercase<S>;

type SnakeCase<S extends string> = S extends `${infer T}${infer U}`
  ? `${T extends Capitalize<T> ? '_' : ''}${Lowercase<T>}${SnakeCase<U>}`
  : S;

type Primitive = Date | string | number | boolean | null | undefined;

type CaseConvert<T, C extends 'camel' | 'snake'> =  T extends Array<any>
  ? { [K in keyof T]: CaseConvert<T[K], C> }
  : T extends Primitive  ? T
  : T extends object
  ? {
      [K in keyof T as C extends 'camel' | 'snake' & 'camel' ? CamelCase<string & K> : SnakeCase<string & K>]: CaseConvert<T[K], C>;
    }
  : T;

type KeysToCamelCase<T> = T extends Array<any>
  ? { [K in keyof T]: KeysToCamelCase<T[K]> }
  : T extends Primitive  ? T
  : T extends object
  ? {
      [K in keyof T as CamelCase<string & K>]: KeysToCamelCase<T[K]>;
    }
  : T;

type KeysToSnakeCase<T> = T extends Array<any>
  ? { [K in keyof T]: KeysToCamelCase<T[K]> }
  : T extends object
  ? {
      [K in keyof T as SnakeCase<string & K>]: KeysToSnakeCase<T[K]>;
    }
  : T;

然后在扩展模型的主定义中定义

type ModelFromAttributes<T extends object> = { 
  publicAttributes: T;
  toFlattenedObject(to: 'snake'): CaseConvert<MFA<T, 'flat'>, 'snake'>; 
  toFlattenedObject(to: 'camel'): CaseConvert<MFA<T, 'flat'>, 'camel'>;
} & Model &
  Partial<MFA<T>>;

获得适当的智能的方法是

相关问题