-
Notifications
You must be signed in to change notification settings - Fork 12.8k
Intellisense documentation lost for remaining properties in expanded Omit<> mapped type #49909
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
Comments
I just realized a workaround for this, though I'm sure it's not great for performance: //
// 4: Workaround by using `keyof Omit<>`
//
type MyModifiedInterface4_DocumentationPreserved = {
[P in keyof Omit<MyInterface, 'prop3'>]: MyInterface[P];
};
class MyClass4_DocumentationPreserved implements MyModifiedInterface4_DocumentationPreserved {
prop1 = 1; // Hover doc: Documentation for prop1
prop2 = 'a'; // Hover doc: Documentation for prop2
} |
See also #50715. I believe this type of issue is why practically every UI library out there currently copies (or generates) and duplicates most of the type-information and documentation already present in Building a typed UI library is a monumental effort - and updates to standard HTML element/attribute/event types (from official IDL definitions) really ought to come with built-in platform type definitions. (This would be something that imperative types could potentially alleviate as well - imagine you could simply |
Just to add some clarity while I have the information loaded in memory: The reason this happens is that in the checker's |
Ah, here we go! It has to be an //
// 5: Filtered key remapping preserves documentation!
//
type MyModifiedInterface5_DocumentationPreserved = {
[P in keyof MyInterface as Exclude<P, 'prop3'>]: MyInterface[P];
};
class MyClass5_DocumentationPreserved implements MyModifiedInterface5_DocumentationPreserved {
prop1 = 1; // Hover doc: Documentation for prop1
prop2 = 'a'; // Hover doc: Documentation for prop2
} |
type ProperOmit<T, K extends PropertyKey> = {
[P in keyof T as P extends K ? never : P]: T[P];
// ~~~~~~~~~~~~~ P in keyof ... as P ~
}
/**
* Construct a type with the properties of T except for those in type K.
*/
type Omit<T, K extends keyof any> = Pick<T, Exclude<keyof T, K>>;
/**
* From T, pick a set of properties whose keys are in the union K
*/
type Pick<T, K extends keyof T> = {
[P in K]: T[P];
// P in <keyof T> as P
}; |
The issue is if you use key remapping to rewrite key names, you lose documentation attached to the originally referenced property: For example: interface Source {
/** Some documentation */
readonly prop1: string;
}
type Dest1 ={
readonly [TKey in keyof Source]: Source[TKey];
}
type Dest2 = {
readonly [TKey in keyof Source as `${TKey}`]?: Source[TKey];
}
const x: Dest1;
const y: Dest2; If you trigger intellisense on I use this pattern a lot to allow for prefixing of properties that get passed in to sub-components such as in React, but the fact that documentation gets lost sucks and I'd prefer the docs are retained. |
@Tharaxis Do you have a real-world example you can give? My assumption would be that in almost every case, if you're renaming the keys, the original documentation doesn't quite fit anymore. But it would be fantastic to have a way to interpolate it into a new JSDoc comment for the mapped key. |
An example is something like this: Let's say you have a straightforward component like a label which takes some properties. export interface LabelProps {
/** The label color. */
readonly color?: string;
/** The label text. */
readonly text?: string;
}
export type WithLabelProps<TPrefix extends string> = {
readonly [TKey in keyof LabelProps as `${TPrefix}${(TPrefix extends "") ? TKey : Capitalize<TKey>}`]?: LabelProps[TKey];
}
export function getLabelProps<TPrefix extends string>(prefix: TPrefix, props: WithLabelProps<TPrefix>): LabelProps {
//... do conversion.
}
export function Label(props: LabelProps) {
//... do stuff.
} Now you have a second component which is a composite of a couple of components, including let's say two labels, you want to have these properties exposed on the composite component, but prefixed appropriately to not collide. export interface CompositeComponentProps extends
WithLabelProps<"firstLabel">,
WithLabelProps<"secondLabel"> {
readonly someProp?: string;
}
export function CompositeComponent(props: CompositeComponentProps) {
return (
<>
<Label {...getLabelProps("firstLabel", props)} />
<Label {...getLabelProps("secondLabel", props)} />
</>
);
} Then in a component that is consuming the composite component you can do the following: export function MyComponent() {
return (
<CompositeComponent
firstLabelText="Label 1"
secondLabelText="Label 2" />
);
} In this case it would be great if Hope that suffices. |
Ah! Yeah, I think that's a great example of reusing the original doc being useful. |
Bug Report
I'm attempting to use mapped types over an interface and I want the documentation for the properties from the original interface to be preserved when the mapped interface is implemented by a class. This normally is fine unless the mapper removes some keys from the original interface. The example below actually shows that
Omit<>
and a 1-level expansion ofOmit<>
works fine to preserve documentation over the remaining properties. However, if I expand it one more time into a raw mapped type expression, then I suddenly lose documentation.Unfortunately I can't use either of the first two options in the code I'm trying to write because I'm doing conditional typing on the value side of the map.
Thanks folks!
🔎 Search Terms
omit, exclude, mapped types, documentation, tsdoc, jsdoc, properties, implements, class, hover, intellisense, vscode, quick info
🕗 Version & Regression Information
⏯ Playground Link
Playground link with relevant code
💻 Code
🙁 Actual behavior
In VSCode, hovering over
prop1
andprop2
in the MyClass3_DocumentationLost class result in none of the documentation from MyInterface showing.🙂 Expected behavior
In VSCode, in the MyClass3_DocumentationLost class, hovering over the following props should so the corresponding documentation from MyInterface:
prop1
-> "Documentation for prop1"prop2
-> "Documentation for prop2"The text was updated successfully, but these errors were encountered: