Closed
Description
Bug Report
I ran into this issue while testing my code with the new TS 4.7 beta. I didn't see anything in the breaking changes that looked related, so here's an issue!
🔎 Search Terms
- 4.7.0-beta
🕗 Version & Regression Information
- This changed between versions 4.6.2 and 4.7.0-beta
⏯ Playground Link
Playground Link: 4.7.0-dev showing the error and 4.6.2 not showing an error.
💻 Code
interface Bounds {
min: number;
max: number;
}
/**
* A version of T where number-valued properties become Bounds-valued properties and all other
* properties are dropped, e.g. NumericBoundsOf<{a: number, b: string}> = {a: Bounds}.
*/
type NumericBoundsOf<T> = {
[K in keyof T as T[K] extends number | undefined ? K : never]: Bounds;
}
// Works as intended:
type X = NumericBoundsOf<{a: number; b: string}>;
// ^? type X = { a: Bounds; }
function validate<T extends object>(obj: T, bounds: NumericBoundsOf<T>) {
for (const [key, val] of Object.entries(obj)) {
const boundsForKey = bounds[key as keyof NumericBoundsOf<T>];
if (boundsForKey) {
const {min, max} = boundsForKey;
if (min > val || max < val) return false;
}
}
return true;
}
Output
"use strict";
function validate(obj, bounds) {
for (const [key, val] of Object.entries(obj)) {
const boundsForKey = bounds[key];
if (boundsForKey) {
const { min, max } = boundsForKey;
if (min > val || max < val)
return false;
}
}
return true;
}
Compiler Options
{
"compilerOptions": {
"strict": true,
"noImplicitAny": true,
"strictNullChecks": true,
"strictFunctionTypes": true,
"strictPropertyInitialization": true,
"strictBindCallApply": true,
"noImplicitThis": true,
"noImplicitReturns": true,
"alwaysStrict": true,
"esModuleInterop": true,
"declaration": true,
"experimentalDecorators": true,
"emitDecoratorMetadata": true,
"target": "ES2017",
"jsx": "react",
"module": "ESNext",
"moduleResolution": "node"
}
}
🙁 Actual behavior
This code fails to type check in TypeScript 4.7.0-beta:
function validate<T extends object>(obj: T, bounds: NumericBoundsOf<T>) {
for (const [key, val] of Object.entries(obj)) {
const boundsForKey = bounds[key as keyof NumericBoundsOf<T>];
if (boundsForKey) {
const {min, max} = boundsForKey;
// ~~~ Property 'min' does not exist on type 'unknown'. ts(2339)
// ~~~ Property 'max' does not exist on type 'unknown'. ts(2339)
if (min > val || max < val) {
return false;
}
}
}
return true;
}
It does pass the type checker in TS 4.6.2, as I believe it should.
🙂 Expected behavior
boundsForKey
should have a type of Bounds
and this should pass the type checker (as it does in TS 4.6.2).
The type of boundsForKey
is displayed as NumericBoundsOf<T>[keyof NumericBoundsOf<T>]
in both versions, but evidently TS 4.7.0-beta resolves this to unknown
whereas TS 4.6.2 resolved it to Bounds
.