-
Notifications
You must be signed in to change notification settings - Fork 13k
Description
π Search Terms
'function property generic parameter inference', 'function generic parameter constraint', 'function property generic parameter'
π Version & Regression Information
- This is the behavior in version 6.0.0-dev.20250805, 5.9.2, 5.0.4 and 4.4.4
β― Playground Link
π» Code
function TestConfig <
TConfig extends { a?: any, b?: any, c?: any }
>(
config: TConfig,
test: keyof Omit<TConfig, 'a' | 'b'> extends never ? true : false
) {
console.log(config, test);
}
// Example 1: This works correctly
TestConfig({
a: "hello",
b: 123,
}, true); // β
No error - correctly infers only 'a' and 'b' properties
// Example 2: This fails incorrectly
TestConfig({
a: "hello",
b: function () { return 123 },
}, true); // β Error: Argument of type 'true' is not assignable to parameter of type 'false'
export type BadValidator<TConfig> = `'${keyof TConfig & string}'`;
export function TestConfig2 <
TConfig extends { a: any, b?: any, c?: any }
>(
config: TConfig,
test: BadValidator<TConfig>,
): void {
console.log(config, test);
}
// Example 3 - correct
TestConfig2({
a: "hello",
b: 123
}, "xxx"); // β Error: '"xxx"' not assignable to '"'a'" | "'b'"' (correct)
// Example 4 - incorrect
TestConfig2({
a: "hello",
b: function () { return 123 }
}, "xxx"); // β Error: '"xxx"' not assignable to '"'a'" | "'b'" | "'c'"' (incorrect - includes 'c')
π Actual behavior
-
Conditional types (Example 2): When an object contains function properties, TypeScript falls back to the generic constraint type
({ a?: any, b?: any, c?: any })
, causingkeyof Omit<TConfig, 'a' | 'b'>
to include 'c' and not extendnever
, making the test parameter require false instead of true. -
Template literal types (Example 4): When an object contains function properties, TypeScript uses the constraint keys instead of the actual object keys, causing
BadValidator<TConfig>
to become"'a'" | "'b'" | "'c'"
instead of just"'a'" | "'b'"
.
π Expected behavior
Conditional types (Example 2): Both objects should be inferred as having only the properties they actually contain., Both objects only have 'a' and 'b' properties, so keyof Omit<TConfig, 'a' | 'b'>
should extend never
, making the test parameter accept true in both cases.
Template literal types (Example 4): Both objects should generate the same template literal union. Both objects only have 'a' and 'b' properties, so BadValidator<TConfig>
should be "'a'" | "'b'"
in both cases.
Additional information about the issue
Description
When an object contains function properties, TypeScript's type inference incorrectly falls back to the generic constraint instead of using the actual inferred type. This affects conditional types, template literal types, and other type-level computations.
Impact
This bug prevents implementing compile-time validation that uses conditional types or template literals to analyze the structure of objects with function properties.
Workaround
Adding as const
forces correct inference:
TestConfig2({
a: "hello",
b: function () { return 123 },
} as const, true); // β
Works correctly
Unfortunately this is not a good developer experience for a public library.