Reputation: 842
In the following, c: 3 should be a type error, but it is not. How can it be achieved?
export type ExcludeUndefined<T> = T extends undefined ? never : T
export type GetRequiredKeys<T> = { [K in keyof T]: undefined extends T[K] ? never : K }[keyof T]
export type KeepRequiredKeys<t> = {
[k in ExcludeUndefined<GetRequiredKeys<t>>]-?: undefined extends t[k] ? never : t[k]
}
const ok: KeepRequiredKeys<{
b: 1
c?: number | undefined
}> = { b: 1, c: 3 } // this type errors as expected
const bad: () => KeepRequiredKeys<{
b: 1
c?: number | undefined
}> = () => ({ b: 1, c: 3 }) // c: 3 should be type error, is not
Upvotes: 4
Views: 819
Reputation: 27284
You're in luck. The linked issue is from 6 years ago (!) but just got solved last week. This PR improves the "widening" behavior. Take a look at the code in your original question, run through the updated compiler -- the last line, which you mark as "should be an error", is now correctly flagged as an error.
It sounds like they want to land the PR in v4.1, but I wouldn't necessarily hold the team to that.
Upvotes: 1
Reputation: 7284
You're correct in that it relates to https://github.com/microsoft/TypeScript/issues/241 --- it is a type widening issue. The return value is inferred and then the whole function expression is type checked whether it's assignable to the shape (type) of the const variable.
Unfortunately, there's nothing you can do dynamically except manually annotate the return value of the callback so that it's no longer inferred, which somewhat defeats the purpose in most cases.
Upvotes: 1