question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Compiled .d.ts output for `Omit` is verbose and semantically inconsistent

See original GitHub issue

TypeScript Version: 3.7.1-rc (but not appreciably different in 3.5.1)

Search Terms: Omit, Pick, verbose, long

Code

// HtmlAttrs.ts
export type InputAttrs = React.InputHTMLAttributes<HTMLInputElement>;

// TextFieldWidget.tsx
import { InputAttrs } from '../../utils/HtmlAttrs';
export interface TextFieldProps {
  onChange?: (e: React.ChangeEvent<HTMLInputElement> & { isComposing: boolean }) => void;
}
export const TextFieldWidget = React.forwardRef<HTMLInputElement, TextFieldProps & Omit<InputAttrs, 'onChange'>>((props, ref) => {};

// Output in TextFieldWidget.d.ts
export declare const TextFieldWidget: React.ForwardRefExoticComponent<TextFieldProps & Pick<InputAttrs, "children" | "dir" | "form" | "slot" | "style" | "title" | "hidden" | "pattern" | "disabled" | "defaultChecked" | "defaultValue" | "suppressContentEditableWarning" | "suppressHydrationWarning" | "accessKey" | "className" | "contentEditable" | "contextMenu" | "draggable" | "id" | "lang" | "placeholder" | "spellCheck" | "tabIndex" | "inputMode" | "is" | "radioGroup" | "role" | "about" | "datatype" | "inlist" | "prefix" | "property" | "resource" | "typeof" | "vocab" | "autoCapitalize" | "autoCorrect" | "autoSave" | "color" | "itemProp" | "itemScope" | "itemType" | "itemID" | "itemRef" | "results" | "security" | "unselectable" | "aria-activedescendant" | "aria-atomic" | "aria-autocomplete" | "aria-busy" | "aria-checked" | "aria-colcount" | "aria-colindex" | "aria-colspan" | "aria-controls" | "aria-current" | "aria-describedby" | "aria-details" | "aria-disabled" | "aria-dropeffect" | "aria-errormessage" | "aria-expanded" | "aria-flowto" | "aria-grabbed" | "aria-haspopup" | "aria-hidden" | "aria-invalid" | "aria-keyshortcuts" | "aria-label" | "aria-labelledby" | "aria-level" | "aria-live" | "aria-modal" | "aria-multiline" | "aria-multiselectable" | "aria-orientation" | "aria-owns" | "aria-placeholder" | "aria-posinset" | "aria-pressed" | "aria-readonly" | "aria-relevant" | "aria-required" | "aria-roledescription" | "aria-rowcount" | "aria-rowindex" | "aria-rowspan" | "aria-selected" | "aria-setsize" | "aria-sort" | "aria-valuemax" | "aria-valuemin" | "aria-valuenow" | "aria-valuetext" | "dangerouslySetInnerHTML" | "onCopy" | "onCopyCapture" | "onCut" | "onCutCapture" | "onPaste" | "onPasteCapture" | "onCompositionEnd" | "onCompositionEndCapture" | "onCompositionStart" | "onCompositionStartCapture" | "onCompositionUpdate" | "onCompositionUpdateCapture" | "onFocus" | "onFocusCapture" | "onBlur" | "onBlurCapture" | "onChangeCapture" | "onBeforeInput" | "onBeforeInputCapture" | "onInput" | "onInputCapture" | "onReset" | "onResetCapture" | "onSubmit" | "onSubmitCapture" | "onInvalid" | "onInvalidCapture" | "onLoad" | "onLoadCapture" | "onError" | "onErrorCapture" | "onKeyDown" | "onKeyDownCapture" | "onKeyPress" | "onKeyPressCapture" | "onKeyUp" | "onKeyUpCapture" | "onAbort" | "onAbortCapture" | "onCanPlay" | "onCanPlayCapture" | "onCanPlayThrough" | "onCanPlayThroughCapture" | "onDurationChange" | "onDurationChangeCapture" | "onEmptied" | "onEmptiedCapture" | "onEncrypted" | "onEncryptedCapture" | "onEnded" | "onEndedCapture" | "onLoadedData" | "onLoadedDataCapture" | "onLoadedMetadata" | "onLoadedMetadataCapture" | "onLoadStart" | "onLoadStartCapture" | "onPause" | "onPauseCapture" | "onPlay" | "onPlayCapture" | "onPlaying" | "onPlayingCapture" | "onProgress" | "onProgressCapture" | "onRateChange" | "onRateChangeCapture" | "onSeeked" | "onSeekedCapture" | "onSeeking" | "onSeekingCapture" | "onStalled" | "onStalledCapture" | "onSuspend" | "onSuspendCapture" | "onTimeUpdate" | "onTimeUpdateCapture" | "onVolumeChange" | "onVolumeChangeCapture" | "onWaiting" | "onWaitingCapture" | "onAuxClick" | "onAuxClickCapture" | "onClick" | "onClickCapture" | "onContextMenu" | "onContextMenuCapture" | "onDoubleClick" | "onDoubleClickCapture" | "onDrag" | "onDragCapture" | "onDragEnd" | "onDragEndCapture" | "onDragEnter" | "onDragEnterCapture" | "onDragExit" | "onDragExitCapture" | "onDragLeave" | "onDragLeaveCapture" | "onDragOver" | "onDragOverCapture" | "onDragStart" | "onDragStartCapture" | "onDrop" | "onDropCapture" | "onMouseDown" | "onMouseDownCapture" | "onMouseEnter" | "onMouseLeave" | "onMouseMove" | "onMouseMoveCapture" | "onMouseOut" | "onMouseOutCapture" | "onMouseOver" | "onMouseOverCapture" | "onMouseUp" | "onMouseUpCapture" | "onSelect" | "onSelectCapture" | "onTouchCancel" | "onTouchCancelCapture" | "onTouchEnd" | "onTouchEndCapture" | "onTouchMove" | "onTouchMoveCapture" | "onTouchStart" | "onTouchStartCapture" | "onPointerDown" | "onPointerDownCapture" | "onPointerMove" | "onPointerMoveCapture" | "onPointerUp" | "onPointerUpCapture" | "onPointerCancel" | "onPointerCancelCapture" | "onPointerEnter" | "onPointerEnterCapture" | "onPointerLeave" | "onPointerLeaveCapture" | "onPointerOver" | "onPointerOverCapture" | "onPointerOut" | "onPointerOutCapture" | "onGotPointerCapture" | "onGotPointerCaptureCapture" | "onLostPointerCapture" | "onLostPointerCaptureCapture" | "onScroll" | "onScrollCapture" | "onWheel" | "onWheelCapture" | "onAnimationStart" | "onAnimationStartCapture" | "onAnimationEnd" | "onAnimationEndCapture" | "onAnimationIteration" | "onAnimationIterationCapture" | "onTransitionEnd" | "onTransitionEndCapture" | "size" | "multiple" | "list" | "step" | "autoFocus" | "type" | "height" | "formAction" | "formEncType" | "formMethod" | "formNoValidate" | "formTarget" | "name" | "value" | "width" | "alt" | "crossOrigin" | "src" | "checked" | "maxLength" | "readOnly" | "accept" | "autoComplete" | "capture" | "max" | "min" | "minLength" | "required"> & React.RefAttributes<HTMLInputElement>>;

Expected behavior: I expect the .d.ts definition to mimic the structure of the original definition, i.e. to use Omit rather than Pick. In particular, this changes the self-documenting semantic of using Omit. The original definition is intended to make it obvious that the signature of the onChange 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 a Pick statement. This defeats the self-documenting semantic that is intended by using Omit. It is very confusing to library consumers – “why is there such a gigantic Pick type with 200+ entries???” And it actually changes the intended semantic, as the meaning of the Omit-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.

Issue Analytics

  • State:open
  • Created 4 years ago
  • Reactions:7
  • Comments:17 (10 by maintainers)

github_iconTop GitHub Comments

3reactions
AnyhowStepcommented, Dec 29, 2019

Or we can just have some way of telling the compiler to not expand a type alias, https://github.com/microsoft/TypeScript/issues/35654

2reactions
weswighamcommented, Mar 25, 2020

@ExE-Boss I edit sniped you with a corrected declaration that does work.

Read more comments on GitHub >

github_iconTop Results From Across the Web

Rob Palmer on Twitter: "My favourite feature in the TypeScript 4.2 ...
Compiled .d.ts output for `Omit` is verbose and semantically inconsistent · Issue #34793 · micros... TypeScript Version: 3.7.1-rc (but not appreciably ...
Read more >
stop typescript compiler from creating .d.ts files - Stack Overflow
I want typescript to not create .d.ts files when compiling. This is because it causes a "duplicate Identifier" error on all class names....
Read more >
Print unions resulting from keyof operations with keyof if possible by ...
Since this isn't a high-priority fix (I think), I'd like to see a more ... Compiled .d.ts output for Omit is verbose and...
Read more >
Optimize Options (Using the GNU Compiler Collection (GCC))
Compiling multiple files at once to a single output file mode allows the compiler to use ... Omit the frame pointer in functions...
Read more >
Writing R Extensions - The Comprehensive R Archive Network
For packages which contain code to be compiled, a computing environment ... the output of tools::checkFF("mypkg", verbose=TRUE) , especially if the ...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found