-
Notifications
You must be signed in to change notification settings - Fork 12.6k
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
Compiled .d.ts output for Omit
is verbose and semantically inconsistent
#34793
Comments
Somewhat related to #34556 If there was a way to control TS to alias, or not alias a type, one could just say, alias type Omit<T, K> = Pick<T, Exclude<keyof T, K>> Then, output will always use Or, nonAlias type Omit<T, K> = Pick<T, Exclude<keyof T, K>> Then, the output will always use Right now, whether a type alias gets "expanded" or not is a bit unintuitive. Often times, I have to use the |
@AnyhowStep thanks for your comment. I’m wondering from it — do you know of any existing workarounds to influence this unwanted behavior, or is your proposal necessary first? |
Just write your own type Omit2<T, K extends keyof T> = {
[P in Exclude<keyof T, K>]: T[P];
};
import * as React from "react";
// HtmlAttrs.ts
export type InputAttrs = React.InputHTMLAttributes<HTMLInputElement>;
// TextFieldWidget.tsx
export interface TextFieldProps {
onChange?: (e: React.ChangeEvent<HTMLInputElement> & { isComposing: boolean }) => void;
}
/*
const TextFieldWidget: React.ForwardRefExoticComponent<
TextFieldProps &
Omit2<InputAttrs, "onChange"> &
React.RefAttributes<HTMLInputElement>
>
*/
export const TextFieldWidget =
React.forwardRef<
HTMLInputElement,
TextFieldProps & Omit2<InputAttrs, 'onChange'>
>((props, ref) => { }; It's a pretty difficult problem, in the general case. Usually, the emit is not considered part of the behaviour of these type alises/operators. However, when you add humans into the mix, suddenly, the emit kind of matters (readability). Because sometimes, a 2 line emit is more readable than a 100+ line emit. You can have two types that have the exact same behaviour, except for having different emit. The human using the types has to decide if type I actually unit test the |
@AnyhowStep I’m actually using my own Omit definition as many of the modules depending on this module don’t have a new enough TSC to assume the built-in type. But the emit is just as verbose, whether I use the TS-provided def or my own. Any other ideas? :( |
This output isn't verbose, though Omit2<InputAttrs, "onChange"> What does your definition look like? |
@AnyhowStep that's how I got here -- both the built-in Omit and mine give the same verbose output. I'm using the same definition that was "omitted" (lol) in TS 2.8 as "trivial":
|
I guess my expectation is that the compiler resolves (sorry if wrong term) the Omit type later when it's used, rather than resolving it in the .d.ts emit. |
Did you try my Omit2 implementation? |
It seems likely that this has the same root cause as #34777 -- specifically: #34777 (comment) |
@RyanCavanaugh this got marked as 3.7.2 milestone but definitely did not go in 3.7.2. Just want to make sure that doesn't make it fall into some abyss... |
I think we should start with special‑casing This is because #35889 won’t work due to reasons outlined in #35889 (comment) by @dragomirtitian:
|
Or we can just have some way of telling the compiler to not expand a type alias, |
IMO Expanding type alias, especially for assignment of a variable type or a function return type, seems to be a current design limitation of checker/transformer/syntax.
Explicit alias/non-alias flag on the |
@RyanCavanaugh The easy fix here for us is just to change the lib implementation of type Omit<T, K extends keyof any, Keys extends keyof T = Exclude<keyof T, K>> = {
[P in Keys]: T[P];
}; rather than reusing |
@weswigham See #35889 (comment) for why that didn’t work. |
@ExE-Boss I edit sniped you with a corrected declaration that does work. |
FWIW I wanted to point out that I hit this issue and the proposed workaround of It looks to me like index signature types over a type using Here is a minimal viable reproduction, with commits in the history confirming it works in 3.9 but not 3.8. EDIT: looks like inverting the composition order does not help in 3.8 |
This seems to work as expected in 5.0 -- the
|
TypeScript Version: 3.7.1-rc (but not appreciably different in 3.5.1)
Search Terms: Omit, Pick, verbose, long
Code
Expected behavior:
I expect the .d.ts definition to mimic the structure of the original definition, i.e. to use
Omit
rather thanPick
. In particular, this changes the self-documenting semantic of usingOmit
. The original definition is intended to make it obvious that the signature of theonChange
event is not the standard definition from React, but an augmented type.BTW -- there are more
TextFieldProps
in the real code. The point of defining them in a separate interface that doesn't extend the React props, and then combining the types in the component definition, is to make it easy to document, browse, and create objects containing the component-scope props, without them getting lost in the noise of 260+ native HTML props.Actual behavior:
Instead, the
Omit
definition is replaced by the compile-time equivalent as aPick
statement. This defeats the self-documenting semantic that is intended by usingOmit
. It is very confusing to library consumers -- "why is there such a giganticPick
type with 200+ entries???" And it actually changes the intended semantic, as the meaning of theOmit
-based type is intended to be evaluated by the typescript compiler when the user of the library compiles, not when the library itself is compiled. e.g. if a new version of@types/react
includes a new event type, I should not have to recompile/redistribute my library to support it.I don't-know-I-don't-know WAY MORE about compilers than I know, so I am certainly open to education on this, but both of the issues raised here do seem like (subtle) bugs to me.
The text was updated successfully, but these errors were encountered: