-
Notifications
You must be signed in to change notification settings - Fork 619
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
✨ TypeScript SDK: resolve introspection by reference #8676
Comments
I am very excited for this. Wanted to point out some incorrect assumptions with this though.
The differences between The
A very simple usage of the type system would be type PartialUser = Partial<User>
type Partial<T extends object> = {
[K in keyof T]?: T[K]
} That iterates through all of the properties of type PartialUser = Partial<User> is the same as type PartialUser = {
name?: string
age?: number I'm not suggesting having full support for these types, but just wanted to expand your knowledge on them a little bit more |
I totally agree! However in the context of dagger, an
Of course! I tried to kept it scope of what we want to support. It would be too much of work for now to support specific |
This is where some of my concerns come in on the ts-sdk side, since most thinks, it seems (from my perspective, anyway) is based on go, and not on typescript. It's the same as the I would assume that the majority of people using the typescript sdk, would have a lot more experience with typescript than go, so having some go'isms creep into the ts-sdk feels a little out of place. |
So you think I should support both If |
I don't really know enough about a "dagger interface" to be of much help for that question, but I would think that
I'm just saying that some people would instinctively use |
UPDATE I'm dropping some tests I did to check if importing module could help on the introspection and it seems were very limited with the current configuration: This is the example I've used: Code// index.ts
import { func, object } from "../../../../introspector/decorators/decorators.js"
import { Data, defaultEnum, TestEnum } from "./types.js"
/**
* References class
*/
@object()
export class References {
@func()
data: Data[]
constructor(data: Data[] = []) {
this.data = data
}
@func()
addData(data: Data): References {
this.data.push(data)
return this
}
@func()
dumpDatas(): Data[] {
return this.data
}
@func()
testEnum(test: TestEnum = defaultEnum): TestEnum {
return test
}
@func()
testEnumStatic(test: TestEnum = TestEnum.A): TestEnum {
return test
}
@func()
testDefaultValue(foo: string = "a"): string {
return foo
}
} // type.ts
/**
* Data
*/
export type Data = {
/**
* Item 1
*/
item1: string
item2: number
}
/**
* Test Enum
*/
export enum TestEnum {
/**
* Field A
*/
A = "a",
/**
* Field B
*/
B = "b",
}
export const defaultEnum = TestEnum.A
[
[Module: null prototype] {
TestEnum: { A: 'a', B: 'b' },
defaultEnum: 'a'
},
[Module: null prototype] { References: [class References] }
] However, this doesn't include types and interfaces.
TypeError: 'caller', 'callee', and 'arguments' properties may not be accessed on strict mode functions or the arguments objects for calls to them Based on some research, it's a normal behaviour and we shouldn't disable strict mode to access these data.
In conclusion I can use the import logic to actually resolve exported value, which can be useful for default value resolution but I'm pretty much stuck on the introspection side. I'll need to do it with the basic TypeScript compiler API. I'll continue following this path, if by any chance there's a better way, please let me know, I'm writing code that should be easily replacable for that purpose. |
If you are just using javascript, then you definitely won't access to the types. There is also a way to get the compiler api to simplify a type for you. So it would take something like type User = {name: string, age: number}
type PartialUser = Partial<User> and give you type PartialUser = {name?: string, age?: number} I can't remember the exact way off of the top of my head, but it did involve getting the symbols for the types IIRC |
This will definitely be helpful! I almost at this part haha |
That would also simplify the 'do we use There is also ts-morph which may help out in using the compiler api see: https://stackoverflow.com/a/68623960 This may help as well. The fact that its a cli doesn't help much, but it does basically achieve what you need |
I'm currently hitting an issue with I opened an issue, hopefully I can get unblock fast on that because it's another level of difficulity to do an hybrid resolution: dsherret/ts-morph#1580 |
Update related to the Type resolution: I'm having an issue with Basically the idea is:
Because sometime, Here's in the first case, type is correctly resolve but not in the other because the default value seems to impact the type resolution (ts-morph tries to do some inference but it doesn't really work well). @func()
testEnum(test: TestEnum = defaultEnum): TestEnum {
return test
}
@func()
testEnumStatic(test: TestEnum = TestEnum.A): TestEnum {
return test
} "testEnum": {
"name": "testEnum",
"arguments": {
"test": {
"type": {
"kind": "ENUM_KIND",
"name": "TestEnum"
},
"defaultValue": "b"
}
}
},
"testEnumStatic": {
"name": "testEnumStatic",
"arguments": {
"test": {
"type": {
"kind": "OBJECT_KIND",
"name": "References"
},
}
}
}, |
Ts-morph is doing such a weird job on type resolution.. it can resolve a node to a complete different type and it's not consistent...
That's so strange... I've complete the reference propagation, it works perfectly! Now I'm fighting with ts-morph not giving me the correct type for an argument. Same for return type:
And event for properties
|
Fixes dagger#8676. Refactor typedef resolution to be resolve by reference. The introspection starts from the entrypoint (which is the class named the same as the module) and recursively resolve every references. Refactor the TS compiler API to be abstract with a AST that handle lookups, typedef resolutions and default value resolution. Use `imports` to resolve default value that refers to an exported variable. Add custom introspection error. Improve error handling to point to the line that couldn't be introspected. Add supports for native `enum` keyword and `type` native keyword. Simplify overall code abstractions too minimal. Avoid long abstractions functions. Signed-off-by: Tom Chauveau <[email protected]>
Fixes dagger#8676. Refactor typedef resolution to be resolve by reference. The introspection starts from the entrypoint (which is the class named the same as the module) and recursively resolve every references. Refactor the TS compiler API to be abstract with a AST that handle lookups, typedef resolutions and default value resolution. Use `imports` to resolve default value that refers to an exported variable. Add custom introspection error. Improve error handling to point to the line that couldn't be introspected. Add supports for native `enum` keyword and `type` native keyword. Simplify overall code abstractions too minimal. Avoid long abstractions functions. Signed-off-by: Tom Chauveau <[email protected]>
Fixes dagger#8676. Refactor typedef resolution to be resolve by reference. The introspection starts from the entrypoint (which is the class named the same as the module) and recursively resolve every references. Refactor the TS compiler API to be abstract with a AST that handle lookups, typedef resolutions and default value resolution. Use `imports` to resolve default value that refers to an exported variable. Add custom introspection error. Improve error handling to point to the line that couldn't be introspected. Add supports for native `enum` keyword and `type` native keyword. Simplify overall code abstractions too minimal. Avoid long abstractions functions. Signed-off-by: Tom Chauveau <[email protected]>
Fixes dagger#8676. Refactor typedef resolution to be resolve by reference. The introspection starts from the entrypoint (which is the class named the same as the module) and recursively resolve every references. Refactor the TS compiler API to be abstract with a AST that handle lookups, typedef resolutions and default value resolution. Use `imports` to resolve default value that refers to an exported variable. Add custom introspection error. Improve error handling to point to the line that couldn't be introspected. Add supports for native `enum` keyword and `type` native keyword. Simplify overall code abstractions too minimal. Avoid long abstractions functions. Signed-off-by: Tom Chauveau <[email protected]>
Fixes dagger#8676. Refactor typedef resolution to be resolve by reference. The introspection starts from the entrypoint (which is the class named the same as the module) and recursively resolve every references. Refactor the TS compiler API to be abstract with a AST that handle lookups, typedef resolutions and default value resolution. Use `imports` to resolve default value that refers to an exported variable. Add custom introspection error. Improve error handling to point to the line that couldn't be introspected. Add supports for native `enum` keyword and `type` native keyword. Simplify overall code abstractions too minimal. Avoid long abstractions functions. Signed-off-by: Tom Chauveau <[email protected]>
Fixes dagger#8676. Refactor typedef resolution to be resolve by reference. The introspection starts from the entrypoint (which is the class named the same as the module) and recursively resolve every references. Refactor the TS compiler API to be abstract with a AST that handle lookups, typedef resolutions and default value resolution. Use `imports` to resolve default value that refers to an exported variable. Add custom introspection error. Improve error handling to point to the line that couldn't be introspected. Add supports for native `enum` keyword and `type` native keyword. Simplify overall code abstractions too minimal. Avoid long abstractions functions. Signed-off-by: Tom Chauveau <[email protected]>
Fixes dagger#8676. Refactor typedef resolution to be resolve by reference. The introspection starts from the entrypoint (which is the class named the same as the module) and recursively resolve every references. Refactor the TS compiler API to be abstract with a AST that handle lookups, typedef resolutions and default value resolution. Use `imports` to resolve default value that refers to an exported variable. Add custom introspection error. Improve error handling to point to the line that couldn't be introspected. Add supports for native `enum` keyword and `type` native keyword. Simplify overall code abstractions too minimal. Avoid long abstractions functions. Signed-off-by: Tom Chauveau <[email protected]>
The current TypeScript introspector is quite limited and remove a bunch of interesting feature that are natively available and broadly uses by TS engineers such as the complete type system, generics and complex default value.
The global idea is to make the TS SDK DX much more natural to a TS engineer.
For example, this simple snippet isn't working with dagger but should after this improvement:
Right now, you need to create a
class User
, exposing field using decorator and creating a constructor if you don't want to assign fields one by one. It's possible to hack it usingObject.assign
but it's doesn't feels right and it's not commonly used in TypeScript project.Then why don't we just supports the
type
keyword?The answer is simple: how do we know if the type should actually be exposed to the API or not? We managed that with decorators like
@object()
and@func()
buttype
,enum
andinterface
keywords doesn't support decorators.So if we aim to support these keywords, we would need to resolve types & value when they are references.
How does this work
If we reuse the same example, the flow of introspection would be:
Test
Test
class in the user's module code (verify that it's decorated)User
andDEFAULT_NAME
) and register them (for default value it's just resolving what the value is)What does it enable
This would also enable constructing complex nested type without a huge boilerplate such as
And also indeed, resolve default value in a more accurate way.
It would also be an occasion to improve the introspection error handling to give more hint to the user.
For example:
type XXX is referenced in function XXX but isn't exposed to the dagger API. Please decorate your class with @object()
, same for specific types that are not supported yet liketype Status = "online" | "offline"
.Specs of the new supported TS features
type
Can be used to create dagger object but we can imagine supporting union types also (in a simple way for now)
enum
Resolving references would also enabling using native TypeScript eum, instead of our current noisy notation.
It's not perfect though since you cannot add metadata yet, but this can be solved with generic (gonna write an issue soon about that)
💡
interface
keyword will not be supported yet since it's pretty confusing with actual Dagger interface. TSinterface
allows to create data structure liketype
but also containing function, it's more or less aclass
with only public properties and no constructor. Making it very hard to pick in the context of Dagger.Referenced default value
Thanks to #8653, the introspection will be done in 2 phases.
Tasks
The text was updated successfully, but these errors were encountered: