Generated type declarations convert implicit types to any

95 views Asked by At

I am working on a module where I have to do something like this:

    const foo = () => {
        const bar = {
            a: () => bar,

            b: () => bar,

            c: () => bar
        }

        return bar
    }  

    foo().a().b().c()

Even though all types are implicit typescript is awesome and helps me autocomplete.

Since I have to publish to npm, I let tsc generate the types declarations, however this is what I get in the index.d.ts file:

declare const foo: () => {
    a: () => any;
    b: () => any;
    c: () => any;
};

Which means that whoever install my module will not be able to take full advantage of typescript goodness.

Is there an easy way to solve this problem?

1

There are 1 answers

8
captain-yossarian from Ukraine On

You just need to give TS compiler a small tip to help figure out the types:

interface Bar {
  a: () => Bar;
  b: () => Bar;
  c: () => Bar;
}

const foo = () => {
  const bar: Bar = {
    a: () => bar,
    b: () => bar,
    c: () => bar
  }

  return bar
}

const result = foo().a() // Bar

Second option - recursion approach


type Rec<Bar> = Record<keyof Bar, () => Rec<Bar>>

const foo = () => {

  const bar = {
    a: () => bar,
    b: () => bar,
    c: () => bar
  } as const
  type Bar = typeof bar;
  return bar as Rec<Bar>
}

const result = foo().a().b().b().c(); // Record<"a" | "b" | "c", () => Record<"a" | "b" | "c", ...>>

TS compiler puts any here:

declare const result: Readonly<Record<"a" | "b" | "c", () => Readonly<Record<"a" | "b" | "c", any>>>>;

because it is a recursion type. What would you expect to see here?

declare const result: Record<"a" | "b" | "c", () => Record<"a" | "b" | "c",  () => Record<"a" | "b" | "c", () => Record<"a" | "b" | "c", () => Record<"a" | "b" | "c", () => Record<"a" | "b" | "c", () => Record<"a" | "b" | "c",any>>>>>>>;
// ... Infinity

TS should stop somewhere)