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...
If it helps, I have set up an example repo reproducing the issue.