Hoist types in declarations or copy JSDoc comments to all copies of type
Chưa có ai nhận issue này.
Đánh giá
- Độ khó
- 5/5
- Thời gian dự kiến
- Hơn một tuần
- Mức phù hợp với người mới
- 28/100
- Loại issue
- Tính năng
- Độ rõ ràng
- Khá rõ ràng
- Mức độ hoạt động
- Đình trệ
- Công nghệ
- javascript, typescript
- Lĩnh vực
- compilers
Hướng nghiên cứu
Bắt đầu với TypeScript Playground được liên kết và so sánh các kiểu nguồn với các ví dụ khai báo được phát ra trong issue. Theo dõi cách đầu ra khai báo nhân đôi các kiểu generic và làm mất JSDoc, sau đó xác định việc hoàn thành là bảo toàn các mô tả thuộc tính đồng thời giảm cấu trúc kiểu lặp lại không cần thiết mà không thay đổi hành vi runtime.
Do mô hình lập chỉ mục viết ra từ nội dung của issue.
Mô tả
Suggestion
🔍 Search Terms
- jsdoc
- jsdoc zod
- jsdoc missing
- hoist types
✅ Viability 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, new syntax sugar for JS, etc.)
- This feature would agree with the rest of TypeScript's Design Goals
⭐ Suggestion
I stumbled upon problem while using Zod with TypeScript: it loses important JSDoc and produces GIANT declarations for complex schemas when emitting declaration file. This is because TypeScript duplicates the type declarations in generic types, and generic parameters that have default values and could safely be omitted. Neither does it copy JSDocs to duplicated types.
I was thinking how this can be solved and came up with idea of ‘type’ hoisting: compiler can hoist types that will be re-used, under safe names. See motivating example for more details.
Alternative solution to just copy JSDoc descriptions, but this will make already giant declaration even bigger, cause in complex types TypeScript can duplicate same property 5-10 times.
📃 Motivating Example
In the attached playground I defined User and AuthenticatedUser schemas. User has only id property, and AuthenticatedUser has email and inherits id from User. All properties have simple JSDoc comments to explain the meanings.
When working with the original code you can write type Test = AuthenticatedUser['email'], hover over ['email'] and see the JSDoc comment for email property. However, if you use the declaration, and try to do the same thing, TypeScript will no longer show anything but that email is a string (useful, but also not).
This means that if you are writing a node package with some Zod schemas, e.g. you have a monorepo and @org/api-schemas, you cannot store ANY JSDoc comments. This can be a huge deal. Let's try to solve that.
If you look closely into declaration, you can notice that TypeScript copied default parameters of ZodObject that can be safely be omitted, let's do that.
Patch 1
--- a.d.ts 2023-04-30 22:27:58.780166540 +0000
+++ b.d.ts 2023-04-30 22:28:08.536152930 +0000
@@ -1,34 +1,12 @@
import { z } from "zod";
-declare const userSchema: z.ZodObject<
- {
- /** The id of the user */
- id: z.ZodNumber;
- },
- "strip",
- z.ZodTypeAny,
- {
- id: number;
- },
- {
- id: number;
- }
->;
+declare const userSchema: z.ZodObject<{
+ /** The id of the user */
+ id: z.ZodNumber;
+}>;
type User = z.output<typeof userSchema>;
-declare const authenticatedUserSchema: z.ZodObject<
- {
- id: z.ZodNumber;
- email: z.ZodString;
- },
- "strip",
- z.ZodTypeAny,
- {
- id: number;
- email: string;
- },
- {
- id: number;
- email: string;
- }
->;
+declare const authenticatedUserSchema: z.ZodObject<{
+ id: z.ZodNumber;
+ email: z.ZodString;
+}>;
type AuthenticatedUser = z.output<typeof authenticatedUserSchema>;
export { userSchema, authenticatedUserSchema, User, AuthenticatedUser };
Now look let's see how authenticatedUserSchema type is formed.
Zod defines extend method as follows:
extend<Augmentation extends ZodRawShape>(augmentation: Augmentation): ZodObject<objectUtil.extendShape<T, Augmentation>, UnknownKeys, Catchall>;
and objectUntil.extendShape is already compressed by TypeScript to:
type objectUtil.extendShape<A, B> = { [k in keyof (Omit<A, keyof B> & B)]: (Omit<A, keyof B> & B)[k]; }
We can make the following assumptions traversing it down:
Tis the original object we pass toz.objectinuserSchemadeclarationAugmentationis the object we pass toextendUnknownKeysis in default value, can be omittedCatchallis in default value, can be omittedobjectUtil.extendShapeis simple and can be downcompiled (it is also exported, so if we keep track of exports, we can use it directly asz.objectUtil.extendShape, but in this example we assume we don't do that)- Use of
objectUtil.extendShaperequiresTandAugmentation(they need to be hoisted)
Let's hoist types to simple vars:
Patch 2
--- a.d.ts 2023-04-30 22:41:15.311786349 +0000
+++ b.d.ts 2023-04-30 22:41:18.679781850 +0000
@@ -1,9 +1,14 @@
import { z } from "zod";
-declare const userSchema: z.ZodObject<{
+type _a = {
/** The id of the user */
id: z.ZodNumber;
-}>;
+};
+declare const userSchema: z.ZodObject<_a>;
type User = z.output<typeof userSchema>;
+type _b = {
+ /** The email address of the authenticated user */
+ email: z.ZodString;
+};
declare const authenticatedUserSchema: z.ZodObject<{
id: z.ZodNumber;
email: z.ZodString;
Now that both types are hoisted, we can compile use of objectUtil.extendShape:
Patch 3
--- a.d.ts 2023-04-30 22:43:07.891635955 +0000
+++ b.d.ts 2023-04-30 22:47:20.915298094 +0000
@@ -10,8 +10,7 @@
email: z.ZodString;
};
declare const authenticatedUserSchema: z.ZodObject<{
- id: z.ZodNumber;
- email: z.ZodString;
+ [k in keyof (Omit<_a, keyof _b> & _b)]: (Omit<_a, keyof _b> & _b)[k];
}>;
type AuthenticatedUser = z.output<typeof authenticatedUserSchema>;
export { userSchema, authenticatedUserSchema, User, AuthenticatedUser };
That's it.
Final declaration
import { z } from "zod";
type _a = {
/** The id of the user */
id: z.ZodNumber;
};
declare const userSchema: z.ZodObject<_a>;
type User = z.output<typeof userSchema>;
type _b = {
/** The email address of the authenticated user */
email: z.ZodString;
};
declare const authenticatedUserSchema: z.ZodObject<{
[k in keyof (Omit<_a, keyof _b> & _b)]: (Omit<_a, keyof _b> & _b)[k];
}>;
type AuthenticatedUser = z.output<typeof authenticatedUserSchema>;
export { userSchema, authenticatedUserSchema, User, AuthenticatedUser };
And would you look at that, AuthenticatedUser["id"] now has description from JSDocs!
💻 Use Cases
What do you want to use this for?
Preserve JSDocs and make declarations a tiny bit smaller.
What shortcomings exist with current approaches?
More computation for TypeScript when reading types because defaults no longer provided (?). Or maybe type mismatch if say Zod in our example updates ZodObject type, but I feel like it will break in other way anyway.
What workarounds are you using in the meantime?
There is no workaround for that, as far as I am aware.
Well, you can define resulting types by Zod, for example, but then you're losing the beautiful feature of Zod which is type inference, also you put a huge effort into maintaining types. It's not worth it.
🎈 Additional context
- While Zod is prelevant here, this issue (request) is not related to Zod, it's just a good example.
- I originally wanted to report that as a bug, but realised this is probably a feature request.
- Real example of this issue:
@vintl/nuxtoriginal code vs emitted filedist/module.d.ts(viewable through Code tab on npm).
- Ngôn ngữ chính
- Go
- Star
- 111k
- Fork
- 14.4k
- Merge trung bình
- 1 ngày 19 giờ
- Pull request đã merge (30 ngày)
- 117
Hướng dẫn đóng góp
Bắt đầu từ đâu
- Đọc hết issue, rồi đọc hướng dẫn đóng góp của dự án.
- Bình luận trên issue rằng bạn sẽ nhận — tránh hai người làm cùng một việc.
- Fork repository và làm thay đổi trên một nhánh.
- Mở pull request có tham chiếu số hiệu của issue.
Issue khác của microsoft/TypeScript
-
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 88/100
microsoft/TypeScript#64322 · 2 bình luận · 1 reaction · 2 người được giao ·
-
Possible Improvement
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 78/100
microsoft/TypeScript#64278 · 1 bình luận · 1 reaction ·
-
Docs
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 70/100
microsoft/TypeScript#64118 · 1 bình luận ·
-
Độ khó 1/5 Dưới một giờ Mức phù hợp với người mới 88/100
microsoft/TypeScript#64094 ·
-
Docs
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 76/100
microsoft/TypeScript#63959 · 5 bình luận ·
Tất cả issue của microsoft/TypeScript
Issue tương tự
-
optimization optimization:agents-md-curator
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 86/100
githubnext/gh-aw-cao#13143 ·
-
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 84/100
blinklabs-io/bursa#904 ·
-
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 84/100
yanet-platform/ipfw-go#129 ·
-
bug confmap/provider/googlesecretmanagerprovider needs triage
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 72/100
open-telemetry/opentelemetry-collector-contrib#51273 · 2 bình luận ·
-
bug: AI Gateway client filter lists "Unknown" twice when NULL and literal Unknown clients coexist Đang mởbug
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 90/100