Edit:
I believe the real problem is that there isn't / I don't know a type for an empty object?! The following, surprisingly, is not an error:
const empty = {} as const;
const xx: typeof empty = {
ho: 'hi'
}
while this is
const notEmpty = {
hasKey: 'andValue'
} as const;
const yy: typeof notEmpty = {
ho: 'hu' // not assignable
}
There is a github issue regarding this problem, which people seem not to take very seriously. I believe it is!! It's totally inconsistent behaviour!!!
I'm trying to build a complex API object with all kinds of properties and functions from a fairly basic input object.
This requires very challenging TypeScript constructs with mapped and conditional types.
I've come pretty far, but am currently challenged by weirdness: I'm using nested utility types at one point, which can result in an effective Pick<T, never>. Since none of the keys inside T can be assigned to never I'd expect the resulting type to be typeof {}. Instead, it's any.
That is extremely unsafe, and currently blows my approach to doing things.
Is there a way to avoid this? Is this a bug, or something that's unavoidable?
Here a simple reproduction:
type Original = {
ho: string;
ha: string;
hu: string;
}
type Derived = Pick<Original, 'ho'>;
const thisIsFine: Derived = { // works fine, as intended
ho: 'yes',
// ha: 'can\'t be assigned'
}
type ShouldBeEmptyObject = Pick<Original, never>;
const shouldntBePossible: ShouldBeEmptyObject = {
anythingGoes: "that's not cool"
}
// More practical example
type AnyObject = {
[key: string]: any
}
// I distinguish paremeter from property by checking if key starts with '$'
type ParameterSegments<T extends AnyObject> = Pick<T, Extract<keyof T, `$${string}`>>;
type PropertySegments<T extends AnyObject> = Omit<T, Extract<keyof T, `$${string}`>>;
const inputObject = { // we'll generates subtypes from this
property1: 'hoho',
property2: 'haha',
$parameter7: 'param'
};
const onlyProperties: PropertySegments<typeof inputObject> = { // works fine
property1: 'juppie', // must be present
property2: 'juchei', // must be present
// property3: "can't be assigned, not in inputObject",
// $parameter7: "can't be assigned, is parameter"
};
const onlyPropertyInput = { // troublemaker
propertyX: 'XxX'
};
const onlyParameters: ParameterSegments<typeof onlyPropertyInput> = { // SHIT
jap: 'no',
$worries: '!',
anything: 'goes',
$nothing: 'isRequired'
};