1

Following up with two of my other questions. I'm working on a library that also depends on a lot of user-provided values. So for its type system to work, there must be some sort of type augmentation.

Styled components for example, does this with module augmentation by allowing the user to create a declarations file.

But I'm having a hard time having this to work in my implementation:
(please note the Library package and Main package comments)

/*
 * ========================
 *   Library package
 * ========================
 */

/*
 * ========================
 *   Control
 * ========================
 */

type ControlSchema<
  ControlName extends string,
  // This is used for inference on MapControlTypes hence why it's unused here
  Type = any
> = {
  name: ControlName
};

type Control = AllControls;

// This should return the names of ALL controls
type ControlNames = Control['name'];

// Converts "Test 1" to "number",  "Test 2" to "string"
type MapControlTypes<Name extends ControlNames, C extends Control = Control> = C extends ControlSchema<Name, infer Type> ? Type : never;

const buildControlSchema = <ControlName extends string, Type, C extends ControlSchema<ControlName, Type> = ControlSchema<ControlName, Type>>(c: C) => c;

/*
 * ========================
 *   Block
 * ========================
 */
type ControlDeclaration<C extends Control = Control> = C extends ControlSchema<infer ControlName> ? { name: ControlName } : never;

type BlockControl = {
  [key: string]: ControlDeclaration;
};

type BlockSchema<BC extends BlockControl> = {
  title: string;
  controls: BC;
};

// Ref: https://stackoverflow.com/questions/71785211/how-to-infer-types-of-an-object-created-from-a-schema
type InferBlockPropsFromControl<BS> = BS extends BlockSchema<infer BC> ? { [P in keyof BC]: MapControlTypes<BC[P]['name']> } : never;

// Ref: https://stackoverflow.com/questions/71785211/how-to-infer-types-of-an-object-created-from-a-schema
const buildBlockSchema = <BC extends BlockControl, B extends BlockSchema<BC> = BlockSchema<BC>>(b: B) => b

/*
 * ========================
 *   Main package (the one that imports the library package)
 * ========================
 */

const firstControl = buildControlSchema<'Test 1', number>({
  name: 'Test 1'
})

const secondControl = buildControlSchema<'Test 2', string>({
  name: 'Test 2'
})

const controls = [
  firstControl,
  secondControl
]

// This must be fed back into Library package
type AllControls = typeof controls[number];

var firstBlock = buildBlockSchema({
  title: "First block",
  controls: {
    testOne: {
      name: 'Test 1'
    },
    testTwo: {
      name: 'Test 2'
    }
  }
});

var secondBlock = buildBlockSchema({
  title: "Second block",
  controls: {
    second: {
      name: 'Test 2'
    },
    block: {
      name: 'Test 1'
    }
  }
});

const blocks = [
  firstBlock,
  secondBlock
]

// This must be fed back into Library package
type AllBlocks = typeof blocks[number];

type NewSchemaProps = InferBlockPropsFromControl<typeof firstBlock>;

/* Expected result:
  NewSchemaProps = {
    testOne: number;
    testTwo: string;
  }
*/

The problem is... It works fine through the typescript playground (look at lines 79, 82, 107 how the autocomplete works and also inferred typing), but as soon as I install my library as a package. It doesn't work compared to the playground...

Playground(Does work):
Playgorund example

Package(Doesn't work):
Package example


If it helps, I have set up an example repo reproducing the issue.

assisrMatheus
  • 445
  • 1
  • 5
  • 17

1 Answers1

-1

In your typescript playground, you've defined the type of AllControls only after creating your control array

type Control = AllControls;

// This should return the names of ALL controls
type ControlNames = Control['name'];

...

// This must be fed back into Library package
type AllControls = typeof controls[number];

In the repository, you only declare MyControls but you haven't overwritten AllControls and you can't once the type is officially declared

export const controls = [firstControl, secondControl];

export type MyControls = typeof controls[number];

There may be a way to pass control types to buildBlockSchema<typeof MyControls,..>({})

Bugbeeb
  • 2,021
  • 1
  • 9
  • 26
  • I override`AllControls` here: https://github.com/AssisrMatheus/type-augmentation-example/blob/main/main/src/library.d.ts – assisrMatheus Aug 30 '22 at 14:03
  • Either way you're walking in circles, the type has already been declared. You cannot import it, redeclare it, and then expect the original file it was imported from to reflect those changes. – Bugbeeb Aug 31 '22 at 17:24