Skip to content

first attempt to widen literals #3

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion src/compiler/checker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13490,12 +13490,15 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
const optionalMask = modifiers & MappedTypeModifiers.IncludeOptional ? 0 : SymbolFlags.Optional;
const indexInfos = indexInfo ? [createIndexInfo(stringType, inferReverseMappedType(indexInfo.type, type.mappedType, type.constraintType), readonlyMask && indexInfo.isReadonly)] : emptyArray;
const members = createSymbolTable();
const isSourceInitializerConstAsserted = (type.source.symbol.valueDeclaration?.kind === SyntaxKind.ObjectLiteralExpression || type.source.symbol.valueDeclaration?.kind === SyntaxKind.ArrayLiteralExpression) && isConstAssertion((type.source.symbol.valueDeclaration as ObjectLiteralExpression | ArrayLiteralExpression).parent);
for (const prop of getPropertiesOfType(type.source)) {
const checkFlags = CheckFlags.ReverseMapped | (readonlyMask && isReadonlySymbol(prop) ? CheckFlags.Readonly : 0);
const inferredProp = createSymbol(SymbolFlags.Property | prop.flags & optionalMask, prop.escapedName, checkFlags) as ReverseMappedSymbol;
const propType = getTypeOfSymbol(prop);
const isPropInitializerConstAsserted = prop.declarations?.[0]?.kind === SyntaxKind.PropertyAssignment && isConstAssertion((prop.declarations?.[0] as PropertyAssignment).initializer);
inferredProp.declarations = prop.declarations;
inferredProp.links.nameType = getSymbolLinks(prop).nameType;
inferredProp.links.propertyType = getTypeOfSymbol(prop);
inferredProp.links.propertyType = !isConstTypeVariable(type.constraintType.type) && !isSourceInitializerConstAsserted && !isPropInitializerConstAsserted ? getBaseTypeOfLiteralType(propType) : propType;
if (
type.constraintType.type.flags & TypeFlags.IndexedAccess
&& (type.constraintType.type as IndexedAccessType).objectType.flags & TypeFlags.TypeParameter
Expand Down
176 changes: 176 additions & 0 deletions tests/cases/compiler/reverseMappedTypesLiteralsInference.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,176 @@
interface WithNestedPropd {
boolean?: boolean;
prop?: string;
value?: number;
nested: {
prop: string;
},
arr: unknown[];
}

declare function withNestedPropd<T extends WithNestedPropd>(props: { [K in keyof T | keyof WithNestedPropd]: T[K] }): T;


// ---------------------------------------- things that now work as they should ----------------------------------------

const wnpd_test0 = withNestedPropd({
// ^?
boolean: false,
prop: 'foo',
value: 10,
nested: { prop: 'bar' },
extra: 10,
arr: [1,2,3]
});

const wnpd_test1 = withNestedPropd({
// ^?
boolean: false,
prop: 'foo',
value: 10,
nested: { prop: 'bar' } as { prop: 'bar' },
extra: 10,
arr: [1,2,3]
});

const wnpd_test2 = withNestedPropd({
// ^?
boolean: false,
prop: 'foo',
value: 10,
nested: { prop: 'bar' },
extra: 10,
arr: [1,2,3] as [1,2,3]
});

const wnpd_test3 = withNestedPropd({
// ^?
boolean: false as const,
prop: 'foo',
value: 10,
nested: { prop: 'bar' },
extra: 10,
arr: [1,2,3]
});

const wnpd_test4 = withNestedPropd({
// ^?
boolean: false,
prop: 'foo' as const,
value: 10,
nested: { prop: 'bar' },
extra: 10,
arr: [1,2,3]
});

const wnpd_test5 = withNestedPropd({
// ^?
boolean: false,
prop: 'foo',
value: 10 as const,
nested: { prop: 'bar' },
extra: 10,
arr: [1,2,3]
});

const wnpd_test6 = withNestedPropd({
// ^?
boolean: false,
prop: 'foo',
value: 10,
nested: { prop: 'bar' } as const,
extra: 10,
arr: [1,2,3]
});

const wnpd_test7 = withNestedPropd({
// ^?
boolean: false,
prop: 'foo',
value: 10,
nested: { prop: 'bar' },
extra: 10 as const,
arr: [1,2,3]
});

const wnpd_test8 = withNestedPropd({
// ^?
boolean: false,
prop: 'foo',
value: 10,
nested: { prop: 'bar' },
extra: 10,
arr: [1,2,3] as const
});

const wnpd_test9 = withNestedPropd({
// ^?
boolean: false,
prop: 'foo',
value: 10,
nested: { prop: 'bar' },
extra: 10,
arr: [1,2,3]
} as const);

// ---------------------------------------- things that still don't work ----------------------------------------

const wnpd_test10 = withNestedPropd({
// ^?
boolean: false as false,
prop: 'foo',
value: 10,
nested: { prop: 'bar' },
extra: 10,
arr: [1,2,3]
});

const wnpd_test11 = withNestedPropd({
// ^?
boolean: false,
prop: 'foo' as 'foo',
value: 10,
nested: { prop: 'bar' },
extra: 10,
arr: [1,2,3]
});

const wnpd_test12 = withNestedPropd({
// ^?
boolean: false,
prop: 'foo',
value: 10 as 10,
nested: { prop: 'bar' },
extra: 10,
arr: [1,2,3]
});

const wnpd_test13 = withNestedPropd({
// ^?
boolean: false,
prop: 'foo',
value: 10,
nested: { prop: 'bar' },
extra: 10 as 10,
arr: [1,2,3]
});

const wnpd_test14 = withNestedPropd({
// ^?
boolean: false,
prop: 'foo',
value: 10,
nested: { prop: 'bar' },
extra: 10,
arr: [1,2,3]
} as { boolean: false, prop: 'foo', value: 10, nested: { prop: 'bar' }, extra: 10, arr: [1,2,3] });

// we say goodbye to optional properties
const wnpd_test15 = withNestedPropd({
// ^?
nested: { prop: 'bar' },
extra: 10,
arr: [1,2,3]
});

// WithNestedPropd has no readonly properties, but if it did we would say goodbye to readonly properties too