Open
Description
Bug Report
🔎 Search Terms
string union widening
, string union regression
, union widening
🕗 Version & Regression Information
This regression occurred in 4.8.0, and can be reproduced with the 4.8.0-beta
, and nightly (4.9.0-dev.20220904
) tag in TS Playground. This works with 4.7.4
.
- This changed between versions 4.7.4 and 4.8.0-beta
I feel like this might be related to the Improved inference from binding patterns change
⏯ Playground Link
Playground link with relevant code
To reproduce, hover over the "isWorking" variable to see that the type definition includes a union of the strings. Switch to 4.8.0-beta, and note that the type definition is now just string
.
💻 Code
interface Guard<T> {
(val: unknown): val is T;
}
type ObjectGuard<T> = {
[key in keyof T]: Guard<T[key]>;
};
function isObject(val: unknown): val is Record<string, unknown> {
return val !== undefined && val !== null && typeof val === 'object' && !Array.isArray(val);
}
function isObjectOfShape<T>(
value: unknown,
shape: ObjectGuard<T>,
): value is T {
if (!isObject(value)) {
return false;
}
let validShape = true;
for (const key in shape) {
const guard = shape[key];
if (guard && !guard(value[key])) {
return false;
}
}
return validShape;
}
function createObjectGuard<T>(guard: ObjectGuard<T>) {
return (val: unknown): val is T => isObjectOfShape(val, guard);
}
function asLiteral<T extends (string | boolean | number)[]>(...literals: T) {
return (val: unknown): val is T[number] => {
return literals.includes(val as T[number]);
};
}
// See type of `isWorking` - should include the type key as a union of strings
const isWorking = createObjectGuard({
// ^?
type: asLiteral('these', 'should', 'be', 'a', 'union'),
});
🙁 Actual behavior
String union type is widened to string
.
🙂 Expected behavior
String union type remains a union, rather than widening