Io-ts: Compare runtime types?

Created on 23 Aug 2017  路  10Comments  路  Source: gcanti/io-ts

I need to compare runtime types, i.e. understand whether two types are identical, one a sub-type of the other, or incompatible. It seems that no straightforward way to achieve this was built into the library, or am I missing something?

I'd like to create a function like:

function isSubtype(subtype: Type<A>, supertype: Type<B>): boolean

Currently I can look at the name property, which is good for basic types (e.g. t.string.name === 'string') but must be parsed when dealing with complex types (e.g. t.array(t.boolean).name === 'Array<boolean>'). Any better option?

out of scope

All 10 comments

typescript uses structural typing, so, you must iterate over the fields and compare them.

This is an example of the object's type traversal:

type ObjectType<T extends t.Props> = t.InterfaceType<T> | t.PartialType<T>

function traverseObjectType<T>(typ: t.Type<T>, f: (type: ObjectType<any>) => t.Type<any>): t.Type<T> {
  if (TU.isUnionType(typ)) {
    return t.union(typ.types.map((v: t.Type<any>) => traverseObjectType(v, f)), typ.name)
  } else if (TU.isReadonlyType(typ)) {
    return t.readonly(traverseObjectType(typ.type, f), typ.name) as any
  } else if (TU.isRefinementType(typ)) {
    return t.refinement(traverseObjectType(typ.type, f), typ.predicate, typ.name) as any
  } else if (TU.isInterfaceType(typ) || TU.isPartialType(typ)) {
    return f(typ)
  } else if (TU.isIntersectionType(typ)) {
    return t.intersection(typ.types.map((v: t.Type<any>) => traverseObjectType(v, f)), typ.name)
  } else if (TU.isArrayType(typ)) {
    return t.array(traverseObjectType(typ.type, f), typ.name) as any
  } else if (TU.isDictionaryType(typ)) {
    return t.dictionary(typ.domain, traverseObjectType(typ.codomain, f), typ.name) as any
  }
  return typ
}

Thank you!

TU is yours, right? How did you implement TU.isInterfaceType(typ), for instance?

export function isUnionType(type: t.Type<any>): type is t.UnionType<any, any> {
  return (type as any)._tag === "UnionType"
}

export function isInterfaceType(type: t.Type<any>): type is t.InterfaceType<any> {
  return (type as any)._tag === "InterfaceType"
}

export function isPartialType(type: t.Type<any>): type is t.PartialType<any> {
  return (type as any)._tag === "PartialType"
}

export function isReadonlyType(type: t.Type<any>): type is t.ReadonlyType<any> {
  return (type as any)._tag === "ReadonlyType"
}

export function isIntersectionType(type: t.Type<any>): type is t.IntersectionType<any, any> {
  return (type as any)._tag === "IntersectionType"
}

export function isArrayType(type: t.Type<any>): type is t.ArrayType<any> {
  return (type as any)._tag === "ArrayType"
}

export function isDictionaryType(type: t.Type<any>): type is t.DictionaryType<any, any> {
  return (type as any)._tag === "DictionaryType"
}

export function isRefinementType(type: t.Type<any>): type is t.RefinementType<any> {
  return (type as any)._tag === "RefinementType"
}

Perfect, I'll use your code. Many thanks!

It would be nice that this sort of inspections were officially supported, through a public API. Maybe _tag could be made public. Any thoughts?

_tags are public but undocumented at the moment, I'll gladly accept a PR (even better if it contains a real world example like yours)

@abaco Did you already implement something like isSubtype, mentioned in your question? If so, would you mind to share it, as I'm also considering to extend io-ts with such a functionality. Thanks!

@fabiandev Actually I focused on other priorities and now I'm not sure anymore whether it makes sense to go that way or not, at least for my use case. Besides comparing runtime types I also need to serialize them, and since t.Type can't be serialized directly (because of the validate function) I'm using a JSON representation, that I'm then going to convert to/from t.Type<*>. At that point it's maybe easier for me to compare the JSON representations. At the end of the day I don't have a "real world example" that justifies this development.

@abaco Fair enough. I'm also currently looking into runtime type comparison through serialized JSON data. I'll leave a comment once I've made some progress.

@fabiandev @abaco I know it's been quite a while, but have anyone of you made progress on this? Looking into the same issue..

@antoniusostermann I've created ts-runtime a while ago which uses flow-runtime as runtime type check library, though. Unfortunately I haven't been able to invest more time after the the initial POC was done.

Was this page helpful?
0 / 5 - 0 ratings