Skip to content

Suggestion: type narrowing based on user-defined type guards against properties #35846

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
5 tasks done
uhyo opened this issue Dec 25, 2019 · 2 comments
Open
5 tasks done
Labels
In Discussion Not yet reached consensus Suggestion An idea for TypeScript

Comments

@uhyo
Copy link
Contributor

uhyo commented Dec 25, 2019

Search Terms

user-defined type guards, type predicates, type narrowing, object properties

Suggestion

TS can narrow object types in if statements and other conditional control flows by various means. Particularly, if the object's type is a union type distinguishable by a "tag" (like Option<T> in the below example), we can narrow the type by checking the value of the tag. However, if the check for the tag is done by user-defined type guards against the property (but not the object itself), the object's type is not narrowed.

The suggestion is to make it work even if user-defined type guards are applied to object properties

Use Cases

The "tagged union" pattern is widely used in TS codebases. If tags theirselves are so complicated, we want to utilize user-defined type guards to check them.

Examples

type Option<T> = {
  type: "some"
  value: T
} | {
  type: "none"
}

const isSome = (type: "some" | "none"): type is "some" => type === "some"

declare const option: Option<number>

// Good: option is narrowed in the if block
if (option.type === "some") {
  option.value
}

// Bad: option isn't narrowed
if (isSome(option.type)) {
  // Error: Property 'value' does not exist on type 'Option<number>'.
  option.value
}

// Available workaround
const isSomeObject = <T>(option: Option<T>): option is Extract<Option<T>, { type: "some" }> => option.type === "some"

if (isSomeObject(option)) {
  option.value
}

playground

Checklist

My suggestion meets these guidelines:

  • This wouldn't be a breaking change in existing TypeScript/JavaScript code
  • This wouldn't change the runtime behavior of existing JavaScript code
  • This could be implemented without emitting different JS based on the types of the expressions
  • This isn't a runtime feature (e.g. library functionality, non-ECMAScript syntax with JavaScript output, etc.)
  • This feature would agree with the rest of TypeScript's Design Goals.
@RyanCavanaugh RyanCavanaugh added In Discussion Not yet reached consensus Suggestion An idea for TypeScript labels Jan 14, 2020
@lovasoa
Copy link

lovasoa commented Feb 26, 2020

I recently encountered an error with the following snippet:

function check_object(o: { a?: string }): { a: string } {
  if (!o.a) throw new Error("Invalid object");
  return o;
}

(TypeScript does not narrow the type of o)

Would this proposal fix this error ?

@uhyo
Copy link
Contributor Author

uhyo commented Feb 27, 2020

@lovasoa Unfortunately this suggestion doesn't affect your use case.
Maybe these issues are related: #33205 (comment), #30506, #31755

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
In Discussion Not yet reached consensus Suggestion An idea for TypeScript
Projects
None yet
Development

No branches or pull requests

3 participants