Jason Kuhrt
Jason Kuhrt

Reputation: 842

How to force TypeScript excess property check on function return value

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

Playground link

Upvotes: 4

Views: 819

Answers (2)

Coderer
Coderer

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

dwelle
dwelle

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

Related Questions