microsoft / TypeScript

TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
https://www.typescriptlang.org
Apache License 2.0
100.2k stars 12.38k forks source link

Can't make type predicate functions in mapped types #51948

Open AlanSorrill opened 1 year ago

AlanSorrill commented 1 year ago

Bug Report

🔎 Search Terms

type predicate mapped types union types mapped type predicates

🕗 Version & Regression Information

4.9.3 & 4.9.4

⏯ Playground Link

https://www.typescriptlang.org/play?#code/PTAuAsFMAIHMHsCGAbaBLAztU86QHaQBOioMoAngA4xVGQAmaAxqTAGYCu+zoa8+LO3hFoifNHgAjAFaRe0bv3wAaaEwzMiaALZp8bBtCkUx0DKG35Y0ZGjIlU7NJGQMAXNACSEiJmisGORQ0M6uRgBElDQRAFAgEDBSnDb+pNghBEbw7Lb6MAAsAIxqEQCC2NS09EysZOhYAsimKMjwAO6M6BL0oJxEvlXQVPAY9sqhIqHcvMpY4kY6kBDwDBgRAHTQAKIAbgQZ8Cng6KAN3Rkwvf0SI2N8ApOiiNM8D-ixyMvq8MyeAKr4ZQABRqLDYGAAPAARABC0N+AD5oABeaD4TjIVCIeb4CggDFYp7YSAWfQ2Kj9O6ktTtNBEqQwXRUL5LfBkIx0iDQABSAGVhkR4AAPUzSOS8WJodgACgYvw2mAAKlUvDpYMDRqAZQBKHUAb1i0GNAQEGHgXw2bVgcoVukQsEg-yIyB1AG5YgBfaCuILoWXy5iKjAqmiarGaiy6g1Gk3MM0WyBW+A2wMbPigL7ur2xWLRGBKyDC0AAWVJGAdMDRhpN0AA+mgPOZLOTY8b854AORkYsljCwTtt0JCnT-IJETwWKywIc4MfESct6yz3SQPmgRA6KieDE6RlEIdLDAVx2L6c5-PeHSVyNnatDhtNqet2sd6Cd3QarWD2v2x3O5AzxfE1MAANUwNApC+TwpHgRNxCHLRIEMMpQB3Tg92IC8hnDZBb1RaAaxNR8gOXV8qi7EYI2-FdM0gUiZ1reAqHeDAGIAbQAXSHXZ4DINj0Qw-cuOwmhoDhBFmAIwte3LStoAAHyvG8tUU6BcNvXMtMvQEQTBOpSUhJVkTRABheAdB0AQAGlIAoKEAHl8GaddpwAGXsYgUAAMRcNwoWM5EizIfA1mbadoAAfmgXSBFBRhwTIAAJVwaCIIy1HMyybLsxznIoVzyQ8hwfL8tYjMRRE1Fi-AlXgMoiBICgKuRTx2BQIJcx0oE4v0tgUuQNKMugLKrPwWzTGCrIsGfaxMvAekGFDUkfWLabFHwABrfAOnwLi1AczhQEpM4ptCrB9W9atPRModTIWtxlqwM6wvY-R2GIUI0CICw1A2f73s++gLE4qLoBlUacsmtbzugTa7JyL6frOaKZWcZH2Mh8a7NBl6ZqXGxopq+Lan61LiGGrGJrUYHQAOo6TugAAyQih1rdjgQuKm7LELAAANMAAEn1UzECoewUDQAAvSBIW5ihEU9YXRfFjc7BlyF0YsTGLLGibOMVvnOM8XVUWRGU-CwfwtdAHUQA63B5VJfBu2gdoRE24kZSVc4bZ1IcbugLtYUQIxdhQThIE7HUg+gPmQ6MeGKD5mPPEO46juZ6AlS03P4mAKBBuIDA8yGeWMAGob7sW+gJDxjbtt2-aRos1aQrCmVCVQJTZtgGO0S727a2rtxa7b9a3vwD7RBtv6AanoHSVAUHIqHCHW-rruwfLyuKdptQZSTxG-az3vWpb7Lsfs3f0v3nZixIXhIUPhHchPlne8yizKtTu6LK6suusoYBRMt4DANUKrj1hpYSO28gFXwruTdKNU6oNSai1WO7VkCdVLmJLwGBCrWGKl5ZARkoFhTPtJch-NhaAxnt9Cwno+ZgxgTANqHVIC4JgE5FyBNiGOF8uEEBBEiLGg5hcI+uQfY4mzhzXGMMKEEzBjKfBhDYD8JQEZORQUFFYFYWDTmO5ID7CIKndExjiDG1kcCbinpc4gAALQOICGCUAWAEArRwNAcAoBjpsRABYRAzBNrwBMewNo7QNjxh0MARAwAACsAAGAAzAAdniQAJgABwADYuFgIgcZAi7ElTyPblgdiKD4A+AcEEWYAgKor1CBw2OrCPSXk1FQCB-xQFVIcrsfykIenUPBogTwdDoBlH7siXijYwYVCMSYj0+S+kDPKsMtEVSanF3kO8SEQ5-gjPEKYVG7AATTOgLMowiysKImWUWEYRAzjdWUKgxqiBmpKjUBUeu3BG7tD2qDNEXFQH4MKTo8p2AiCRyHETHqtV6rvOatsYUzBkCcAYLLL56lmLgqqtADmuL4UVTnhsMoBshyeBKaS8lyyVnwrqtsn6uzlBDNATKQ59djnKM2ucs2lz4BzJuaYkZMohy8u6NPbwsQLlXNhdK2swqPSOIcbEIAA

💻 Code

This is an abbreviated example of the bug. Please see playground for actual example. type Test<T,Other> = { [P in keyof T]: () => (T is Other) }

🙁 Actual behavior

Compile time error: A type predicate is only allowed in return type position for functions and methods.

🙂 Expected behavior

The type predicate is in the return type position for a function. These functions should be useable type discriminators.

For wider context, I'm trying to generate type predicate functions for a union object. The first 8 lines of the playground linked above gives an example of this.

fatcerberus commented 1 year ago

I think you just have to remove the parentheses around T is Other.

andrewbranch commented 1 year ago

Type predicates narrow the type of a value. The “abbreviated example” doesn’t make sense because you’re trying to reference a type, but via the playground it’s clear what you’re trying to do is just use a this type in a mapped type, which does make some sense:

type M<T> = { [K in keyof T]: () => this is T }
// A 'this' type is available only in a non-static member of a class or interface.
andrewbranch commented 1 year ago

The really weird thing is that if you fix your syntax mistake in the playground (remove the parens, as @fatcerberus) said, the code does work as you expect, but we still get the error saying that the this type isn’t allowed outside of a class or interface. From poking around old bugs, I think that one message has two purposes:

  1. There are places where a this type is nonsensical
  2. There are places where a this type looks ambiguous

It looks like when the this type is ambiguous, it doesn’t resolve at all (we return the error any). But in some other places, where there is no ambiguity, and there is an object type to speak of, we issue an error but the type does resolve: https://www.typescriptlang.org/play?#code/PTAEDkHsBdQSwHYGc4BMCmoCGoDGAbLJJUSAJ3gWnTIDMtd0AaAKBFACMBXWLhLALYc4Acy6QuJRNgSkOAK3S5Y0AJ4AHTPjjUyWfCwwEsZLeli1IkAFygA3i1BPQACyIAhE7YAUASlAAvAB8oNAucFIkdpxeoAhcQjSgAL4sqXC0oN6WkAB0bkieZH7+Ds6gObkcJgDcjs7sAHoA-GksLIi69IygAGJW9vVO7Agw2Pj4kADu6Kig6lym+KqcSliSmDoA5CSCwmISSEygAJKgYujEQ9hCouKSfrbRAB62YREpbYZKhKag+OYKrZ+pA6rRcns7oc-LlnmwwOVEU4WkA

I’m not 100% sure, but it does make it feel like part of this restriction is artificial and could be lifted.

AlanSorrill commented 1 year ago

Thanks so much for the feedback! @fatcerberus you were spot on, and @andrewbranch thank you for not closing this as a syntax error. Could this be accomplished with an AST transformer?