Prechádzať zdrojové kódy

@joystream/types - createType TS support

Leszek Wiesner 4 rokov pred
rodič
commit
5cc3f6111f
3 zmenil súbory, kde vykonal 54 pridanie a 9 odobranie
  1. 1 1
      types/src/JoyEnum.ts
  2. 12 5
      types/src/JoyStruct.ts
  3. 41 3
      types/src/index.ts

+ 1 - 1
types/src/JoyEnum.ts

@@ -9,7 +9,7 @@ export interface ExtendedEnum<Types extends Record<string, Constructor>> extends
   type: keyof Types & string // More typesafe type for the original Enum property
 }
 
-export interface ExtendedEnumConstructor<Types extends Record<string, Constructor>>
+export interface ExtendedEnumConstructor<Types extends Record<string, Constructor> = Record<string, Constructor>>
   extends EnumConstructor<ExtendedEnum<Types>> {
   create<TypeKey extends keyof Types>(
     registry: Registry,

+ 12 - 5
types/src/JoyStruct.ts

@@ -4,7 +4,8 @@ import { Codec, Constructor, Registry } from '@polkadot/types/types'
 export interface ExtendedStruct<FieldTypes extends Record<string, Constructor>> extends Struct<FieldTypes> {
   getField<FieldKey extends keyof FieldTypes>(key: FieldKey): InstanceType<FieldTypes[FieldKey]>
   getString<FieldKey extends keyof FieldTypes>(key: FieldKey): string
-  cloneValues(): { [k in keyof FieldTypes]: FieldTypes[k] }
+  cloneValues(): { [k in keyof FieldTypes]: InstanceType<FieldTypes[k]> }
+  typeDefs: FieldTypes
 }
 
 // Those getters are automatically added via Object.defineProperty when using Struct.with
@@ -27,12 +28,16 @@ export interface StructConstructor<
 export type ExtendedStructConstructor<FieldTypes extends Record<string, Constructor>> = StructConstructor<
   FieldTypes,
   ExtendedStruct<FieldTypes>
->
+> & {
+  typeDefs: FieldTypes
+}
 
 export type ExtendedStructDecoratedConstructor<FieldTypes extends Record<string, Constructor>> = StructConstructor<
   FieldTypes,
   ExtendedStructDecorated<FieldTypes>
->
+> & {
+  typeDefs: FieldTypes
+}
 
 // Helper for creating extended Struct type with TS-compatible interface
 // It's called JoyStructCustom, because eventually we'd want to migrate all structs to JoyStructDecorated,
@@ -42,6 +47,8 @@ export function JoyStructCustom<FieldTypes extends Record<string, Constructor>>(
   fields: FieldTypes
 ): ExtendedStructConstructor<FieldTypes> {
   return class JoyStructObject extends Struct.with(fields) {
+    static typeDefs = fields
+    typeDefs = JoyStructObject.typeDefs
     // eslint-disable-next-line no-useless-constructor
     constructor(registry: Registry, value?: { [k in keyof FieldTypes]: InstanceType<FieldTypes[k]> }) {
       super(registry, value)
@@ -56,14 +63,14 @@ export function JoyStructCustom<FieldTypes extends Record<string, Constructor>>(
     }
 
     // TODO: Check why would this ever be needed
-    cloneValues(): { [k in keyof FieldTypes]: FieldTypes[k] } {
+    cloneValues(): { [k in keyof FieldTypes]: InstanceType<FieldTypes[k]> } {
       const objectClone = {} as Partial<{ [k in keyof FieldTypes]: Codec }>
 
       super.forEach((v, k) => {
         objectClone[k] = v // shallow copy acceptable ?
       })
 
-      return (objectClone as unknown) as { [k in keyof FieldTypes]: FieldTypes[k] }
+      return objectClone as { [k in keyof FieldTypes]: InstanceType<FieldTypes[k]> }
     }
   }
 }

+ 41 - 3
types/src/index.ts

@@ -1,4 +1,4 @@
-import { RegistryTypes } from '@polkadot/types/types'
+import { Codec, RegistryTypes } from '@polkadot/types/types'
 import common from './common'
 import members from './members'
 import council from './council'
@@ -15,7 +15,10 @@ import media from './media'
 import proposals from './proposals'
 import contentDirectory from './content-directory'
 import { InterfaceTypes } from '@polkadot/types/types/registry'
-import { TypeRegistry } from '@polkadot/types'
+import { TypeRegistry, Text, UInt, Null, bool, Option, Vec, BTreeSet } from '@polkadot/types'
+import { ExtendedEnum } from './JoyEnum'
+import { ExtendedStruct } from './JoyStruct'
+import BN from 'bn.js'
 
 export {
   common,
@@ -57,9 +60,44 @@ export const types: RegistryTypes = {
 export const registry = new TypeRegistry()
 registry.register(types)
 
+// Tweaked version of https://stackoverflow.com/a/62163715 for handling enum variants
+// Based on type (T) like: { a: string; b: number; c: Null; }
+// will create a type like: { a: string } | { b: number } | { c: Null } | "c"
+type EnumVariant<T> = keyof T extends infer K
+  ? K extends keyof T
+    ? T[K] extends Null
+      ? K
+      : { [I in K]: T[I] }
+    : never
+  : never
+
+// Create simple interface for any Codec type (inlcuding JoyEnums and JoyStructs)
+// Cannot handle Option here, since that would cause circular reference error
+type CreateInterface_NoOption<T extends Codec> =
+  | T
+  | (T extends ExtendedEnum<infer S>
+      ? EnumVariant<{ [K in keyof S]: CreateInterface<InstanceType<T['typeDefinitions'][K]>> }>
+      : T extends ExtendedStruct<infer S>
+      ? { [K in keyof S]?: CreateInterface<InstanceType<T['typeDefs'][K]>> }
+      : T extends Text
+      ? string
+      : T extends UInt
+      ? number | BN
+      : T extends bool
+      ? boolean
+      : T extends Vec<infer S> | BTreeSet<infer S>
+      ? CreateInterface<S>[]
+      : any)
+
+// Wrapper for CreateInterface_NoOption that includes resolving an Option
+// (nested Options like Option<Option<Codec>> will resolve to Option<any>, but there are very edge case)
+type CreateInterface<T extends Codec> =
+  | T
+  | (T extends Option<infer S> ? undefined | null | S | CreateInterface_NoOption<S> : CreateInterface_NoOption<T>)
+
 export function createType<TypeName extends keyof InterfaceTypes>(
   type: TypeName,
-  value: any
+  value: InterfaceTypes[TypeName] extends Codec ? CreateInterface<InterfaceTypes[TypeName]> : any
 ): InterfaceTypes[TypeName] {
   return registry.createType(type, value)
 }