Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[BUG] $$typeToString ellipsizes type information #74

Closed
gwhitney opened this issue Sep 18, 2023 · 7 comments
Closed

[BUG] $$typeToString ellipsizes type information #74

gwhitney opened this issue Sep 18, 2023 · 7 comments

Comments

@gwhitney
Copy link

Describe the bug
For complex types, $$typeToString replaces parts of the type description with ...

Code to reproduce
These are the same examples as in #73. I am using the following macro to reflect the types of arrow functions that I am exporting:

export function $implement<Impl>(name: string, expr: Impl) {
   $$define!(name, expr, false, true); // Final `true` is export
   $$ident!(name).reflectedType = $$typeToString!<Impl>();
}

It works fine on

$implement!('square',
   <T>(dep: Dependencies<'multiply', T>) => (z:T) => dep.multiply(z, z))

where square.reflectedType ends up getting the very reasonable value <T>(dep: { multiply: (a: T, b: T) => T; }) => (z: T) => T. However, on

$implement!('sqrt',
   <T>(dep: Dependencies<'equal' | 'conservativeSqrt' | 'unaryMinus', RealType<T>>
       & Dependencies<'zero' | 'complex', T>
       & Dependencies<'absquare' | 're' | 'divideReal', Complex<T>>
       & {
         addTR: Signature<'addReal', T>, 
         addRR: Signature<'add', RealType<T>>,
         addCR: Signature<'addReal', Complex<T>>
      }):
      Signature<'sqrt', Complex<T>> =>
   z => {
      const myabs = dep.conservativeSqrt(dep.absquare(z))
      const r = dep.re(z)
      const negr = dep.unaryMinus(r)
      if (dep.equal(myabs, negr)) {
         // pure imaginary square root; z.im already zero
         return dep.complex(
            dep.zero(z.re), dep.addTR(z.im, dep.conservativeSqrt(negr)))
      }
      const num = dep.addCR(z, myabs)
      const denomsq = dep.addRR(dep.addRR(myabs, myabs), dep.addRR(r, r))
      const denom = dep.conservativeSqrt(denomsq)
      return dep.divideReal(num, denom)
   })

it produces sqrt.reflectedType equal to

<T>(dep: { unaryMinus: (a: RealType<T>) => RealType<T>; conservativeSqrt: (a: RealType<T>) => RealType<T>; equal: (a: RealType<T>, b: RealType<...>) => boolean; } & { ...; } & { ...; } & { ...; }) => (a: Complex<...>) => Complex<...>

so we have lost much of the type information to those ellipses.

Expected behavior
I would like there to be a way to force $$typeToString to produce the entire description of the typescript type of its template argument, without any ellipsization.

Additional context
Typescript 5.1.6, ts-macros 2.4.1, ts-patch 3.0.2

@GoogleFeud
Copy link
Owner

Can you send me the types used in the snippet that causes the ellipsization? I can't replicate it.

@gwhitney
Copy link
Author

OK, I have tried to collect them from the various imports of that source file:

type Deps<T> =  T extends unknown ? { [K in keyof T]: T[K] } : never;
type Dependencies<Name extends SignatureKey<T>, T> = Deps<{[K in Name]: Signature<K, T>}>
type Signature<Name extends SignatureKey<T>, T> = Signatures<T>[Name]
type SignatureKey<T> = keyof Signatures<T>
type AliasOf<Name extends string, T> = T & {aliasOf?: Name}
interface Signatures<T> {
   zero: (a: T) => ZeroType<T>
   one:  (a: T) => OneType<T>
   nan:  (a: T | NaNType<T>) => NaNType<T>
   re:   (a: T) => RealType<T>
   equal:   (a: T, b: T) => boolean
   add:              (a: T, b: T) => T
   unaryMinus:       (a: T) => T
   conj:             (a: T) => T
   subtract:         (a: T, b: T) => T
   multiply:         (a: T, b: T) => T
   square:           (a: T) => T
   absquare:         (a: T) => RealType<T>
   reciprocal:       (a: T) => T
   divide:           (a: T, b: T) => T
   conservativeSqrt: (a: T) => T
   sqrt: (a: T)=> T extends Complex<unknown> ? T : T | Complex<T>
   complex: ((re: T) => Complex<T>) | ((re: T, im: T) => Complex<T>)
   addReal: AliasOf<'add', (a: T, b: RealType<T>) => T>
   divideReal: AliasOf<'divide', (a: T, b: RealType<T>) => T>
}
type ValueIntersectionByKeyUnion<T,  TKey extends keyof T> = {
  [P in TKey]: (k: T[P])=>void
} [TKey] extends ((k: infer I)=>void) ? I : never

export interface AssociatedTypes<T> {
   undefined: {
      type: undefined
      zero: undefined
      one: undefined
      nan: undefined
      real: undefined
   }
   number: {
      type: number
      zero: 0
      one: 1
      nan: typeof NaN
      real: number
   }
   Complex: T extends Complex<infer R> ? {
      type: Complex<R>
      zero: Complex<ZeroType<R>>
      one:  Complex<OneType<R> | ZeroType<R>>
      nan:  Complex<NaNType<R>>
      real: RealType<R>
   } : never
}
type AssociatedTypeNames = keyof AssociatedTypes<unknown>['undefined']
type ALookup<T, Name extends AssociatedTypeNames> = ValueIntersectionByKeyUnion<{
   [K in keyof AssociatedTypes<T>]:
   T extends AssociatedTypes<T>[K]['type'] ? AssociatedTypes<T>[K][Name] : unknown},
   keyof AssociatedTypes<T>>

type ZeroType<T> = ALookup<T, 'zero'> & T
type OneType<T> = ALookup<T, 'one'> & T
type NaNType<T> = ALookup<T, 'nan'>
type RealType<T> = ALookup<T, 'real'>
type Complex<T> = { re: T; im: T; }

I think I have extracted everything so that the snippet should compile; let me know if I have missed anything. Thanks!

@gwhitney
Copy link
Author

Awesome! Looking forward to the next release ;-)

@GoogleFeud
Copy link
Owner

It's already available in v2.4.2!

@josdejong
Copy link

Thanks! Did you actually publish to npm? npm still reports v2.4.1 as latest version.

@GoogleFeud
Copy link
Owner

That's really weird... the action which handles publishing errored with You cannot publish over the previously published versions: 2.4.2 however there's no trace of version 2.4.2 ever existing before this. On top of that, if you go to npm and search for this package, you'll see the latest version as 2.4.2 but if you go to the package page you'll see it as 2.4.1.

I'll be releasing a minor version really soon (most likely by the end of the week), all changes from 2.4.2 will be included in that release!

@josdejong
Copy link

👍 For the time being I'm using the code straight from Github instead of npm, so I'm not blocked.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants