-
Notifications
You must be signed in to change notification settings - Fork 12.8k
Generated d.ts includes implicit any for recursive types #55832
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βm more surprised this doesnβt have a circularity error, since |
I just found a workaround which actually both avoids the need for inferring a /**
* Interface which carries the runtime and compile type data (from the generic type paramater) in a member.
* This is also a constructor so that instances of it can be extended as classes.
* Using classes in this way allows introducing a named type and a named value at the same time, helping keep the runtime and compile time information together and easy to refer to un a uniform way.
* Addationally, this works around https://github.com/microsoft/TypeScript/issues/55832 which causes similar patterns with less explicit types to infer "any" in the d.ts file.
*/
interface Schema<ChildSchema> extends SchemaData<ChildSchema> {
// We don't actually ever call this constructor,
// but having it allows using classes to introduce runtime and named types at the same time, working around https://github.com/microsoft/TypeScript/issues/55832
new (dummy: never): SchemaData<ChildSchema>;
}
/**
* Used as both a class and an interface.
* Helper for declaring Schema.
*/
class SchemaData<T> {
constructor(public readonly data: T) {}
}
/**
* Builder for the Schema interface.
*/
function build<T>(childType: T): Schema<T> {
return class extends SchemaData<T> {
static readonly data = childType;
constructor() {
super(childType);
}
}
}
// example use
class MySchema extends build(() => MySchema) {}
// Strong recursive typing works correctly:
const child: MySchema = MySchema.data().data().data(); The d.ts does not produce declare const MySchema_base: Schema<() => typeof MySchema>;
declare class MySchema extends MySchema_base {
} |
Looking at this again, I think there might be a genuine bug here, just not the one presupposed in the OP; this isn't actually an implicit It seems like TypeScript knows the type is infinitely expanding, and that's totally fine. The problem only arises when it has to generate a declaration for it, at which point it apparently falls over and defaults to |
@fatcerberus That is the bug I was attempting to report. Everything is correct locally, but the generated d.ts contains "any" which was not explicitly in the source anywhere. I'll update it to clarify that it works locally in the description. |
@CraigMacomber I thought so, but I wanted to be sure after you said this:
which isnβt really relevant IMO since thereβs no implicit |
Its implicitly introducing any in the API for my package via the d.ts file. TypeScript has a known design decision/limitation that the API of .dts files doesn't match the local type checking exactly, so the fact that they are different is not always a bug. |
It seems very reasonable to emit a |
#56479 should be included as a test case for this. |
Further, it'd be awesome to emit some sort of error any time dts emit produces an |
## Description To support recursive schema using classes for the schema is useful to work around microsoft/TypeScript#55832 . If we are going to use classes to enable that workaround, and also create a separate simple-tree specific schema system, there are some other interesting approaches which can be taken. If we don't support "Any" as a type allowed in fields, then all schema are reachable from the root. This means we can skip the schema library concept, and just pass in the root schema in the configuration. This has the side-effect of making the actual schema objects available programmatically be the actual class objects declared in the schema file (instead of the classes which those extend, which is what a library would have captured). This same reachability from the root also opens the door to supporting contextual view schema where the same type in different locations in the tree could have different schema subclasses, and its parent would unambiguously determine which to use for it. Thus all together this approach makes it possible to make the tree nodes instances of classes which the user declared. This has several benefits: - Users don't have to use "typeof" or invoke any type meta-functions to get the node types they want to pass around: just use the class/schema name as the type. - Intelisense is much cleaner when referring to types defined in schema: It just uses the class name. or "typeof ClassName". (These simplifications are what resolve the d.ts issue noted above with recursive types) - normal JS/TS type narrowing with `instanceof` can be used with schema defined types. - Its possible to add view/session local state to instances as properties, as well as adding methods by just putting them in the class like any other class. - Since the schema/class defines the API for the node, the implementation of the tree API implicitly gets defined in a way that is trivially extensible for adding new node kinds. TODO: - [X] support recursive times. - [x] implement runtime behavior for object fields. - [X] implement runtime behavior for list indexing. - [x] structurally typed maps and lists. - [x] runtime constructing of hydrated nodes (ensure flex tree is type erased). - runtime constructing and later hydrating of unhydrated nodes. - [x] objects - [ ] map - [ ] list - [x] Generation of view schema from simple schema. - [x] Implement full simple-tree API surface - [ ] Evaluate impact on simple-tree API (for example exposing of prototypes impacting deep equals). - [x] Basic end to end testing - [ ] Full unit testing. ## Breaking Changes The `schematize` API for simple-trees not using the new class based APIs has been renamed to `schematizeOld`: either rename the call site or migrate to the new class based schema. Some types which would have collided have their old simple-tree or flex-tree non class based version's renamed: ```typeScript TreeNodeSchema as FlexTreeNodeSchema, TreeListNode as TreeListNodeOld, Tree as TreeOld, TreeApi as TreeApiOld, TreeView as TreeViewOld, ``` The previous version of all these names are now used for class based APIs.
π Search Terms
recursive any noImplicitAny d.ts
π Version & Regression Information
β― Playground Link
playground link
π» Code
π Actual behavior
Any is implicitly introduced in the d.ts:
Note that the local type checking (not using the d.ts) correctly handles this recursive type without introducing "any", and builds fine with noImplicitAny enabled.
π Expected behavior
Either generate something without implicitly introducing
any
, or generate an error ifnoImplicitAny
is enabled.In this case, the below code generation for the d.ts would work:
Additional information about the issue
No response
The text was updated successfully, but these errors were encountered: