You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Just a heads up, this is gonna be a lengthy issue.
Summary
I would like to see some helper types get implemented, which would simplify writing more complex type annotations. Some of these could probably be implemented just using existing LuaCATS syntax, but others most likely need special support directly embedded into LuaLS.
This issue includes markers to track which feature is implemented and which isn't.
The types in question
never: Marks a function as not returning anything, and marks storing its value as an error (unlike @returns nil).
Keys<T: table<K, V>>: Extracts K, may create a union type
Values<T: table<K, V>>: Extracts V, may create a union type
NonNilable<T>: Removes nil from the union type T, making a value as required.
Partial<T>: Returns a copy of type/class T, but with each field marked as optional / nilable.
Required<T>: The opposite of Partial<T>, marking all fields as required / non-nilable.
Exclude<T, U>: Remove all types in the union U from the union T
FunctionParameters<T: fun(...)>: Get the parameter names and types of a function.
TypeParameters<T>: Returns a tuple of all type parameters passed T
Notes and examples
TypeParameters<SomeGenericType<string, integer[], boolean>>⇾ a tuple [string, integer[], boolean]
Keys<T> and Values<T>⇾ a union type if the keys / values can have multiple types. In other words:
Keys<{ foo: "abc", bar: "xyz" }> should give the type "foo"|"bar"
Values<{ foo: "abc", bar: "xyz" }> should give the type "abc"|"xyz"
FunctionParameters<fun(name: string, age: integer)> should give the type { name: string, age: integer }
If someone only needs the parameter names, they may use Keys<FunctionParameters<T>>
never is useful for communicating to users of a marked function that its return values may change in the future. This is primarily used to communicate to other maintainers / your future self that they / you can change this if they / you want.
Exclude<T, U> should only remove types from T that it can actually find in U. For example:
Exclude<string|number|boolean|function, number|boolean> should give the type string|function
Exclude<string|number|boolean, function|userdata> should give the type string|number|boolean, as neither function nor userdata are in T
Implementation
These types would most likely require implementing some features from TypeScript:
Conditional types (The TS syntax is predicate ? T : U, but if predicate then T else U would probably fit in better with Lua, and potentially allow for elseif down the line if deemed to be necessary or just a good addition; plus, LuaCATS already uses : as the inheritance-operator and ? as the optional/nilable-operator). Examples:
The first type of the tuple [string, number, boolean] could be accessed with [string, number, boolean][1]
The third parameter name biz of the function fun(foo, bar, biz, baz) could be retrieved with Keys<FunctionParameters<fun(foo, bar, biz, baz)>>[3]
The type of the biz field of the table {foo: "bar", biz: "baz"} could be accessed with {foo: "bar", biz: "baz"}["biz"]
Alternatively, instead of fully implementing these features, it might be enough to "simply" hard-code types like Keys and Values. It might also be a good idea to investigate allowing to write types in Lua (perhaps via plug-ins) instead.
If these are not implemented via hard-coding, the following keywords from TypeScript would most likely have to be implemented as well
in (for collection-like types)
keyof
Once that's all done, some of them could be the implemented like this:
--- These were partially taken and adapted from the TypeScript docs, see:--- https://www.typescriptlang.org/docs/handbook/utility-types.html---@aliasKeys<T>keyof T---@aliasValues<T>T[keyof T]--- ...---@aliasNonNilable<T>if T : nil then never else T---@aliasPartial<T>{ [P in Keys<T>]?: T[P]|undefined }---@aliasRequired<T>{ [P in keyof T]: NonNilable<T[P]> }---@aliasExclude<T, U> if T : U then never else T--- Not sure how this would be implemented...---@aliasFunctionParameters<T>???--- Same goes for this...---@aliasTypeParameters<T>???
Considerations
I am decently familiar with LPEG grammars, so I could probably help make all of this happen if given some pointers in the right directions.
Note
Just a heads up, this is gonna be a lengthy issue.
Summary
I would like to see some helper types get implemented, which would simplify writing more complex type annotations. Some of these could probably be implemented just using existing LuaCATS syntax, but others most likely need special support directly embedded into LuaLS.
This issue includes markers to track which feature is implemented and which isn't.
The types in question
never: Marks a function as not returning anything, and marks storing its value as an error (unlike@returns nil).Keys<T: table<K, V>>: ExtractsK, may create a union typeValues<T: table<K, V>>: ExtractsV, may create a union typeNonNilable<T>: Removesnilfrom the union typeT, making a value as required.Partial<T>: Returns a copy of type/classT, but with each field marked as optional / nilable.Required<T>: The opposite ofPartial<T>, marking all fields as required / non-nilable.Exclude<T, U>: Remove all types in the unionUfrom the unionTFunctionParameters<T: fun(...)>: Get the parameter names and types of a function.TypeParameters<T>: Returns a tuple of all type parameters passedTNotes and examples
TypeParameters<SomeGenericType<string, integer[], boolean>>⇾ a tuple[string, integer[], boolean]Keys<T>andValues<T>⇾ a union type if the keys / values can have multiple types. In other words:Keys<{ foo: "abc", bar: "xyz" }>should give the type"foo"|"bar"Values<{ foo: "abc", bar: "xyz" }>should give the type"abc"|"xyz"The
Partial-type should work like this:Input:
Output:
{ user_name?: string, email_address?: string, verified?: boolean, extra_data?: table<string, Any> }FunctionParameters<fun(name: string, age: integer)>should give the type{ name: string, age: integer }Keys<FunctionParameters<T>>neveris useful for communicating to users of a marked function that its return values may change in the future. This is primarily used to communicate to other maintainers / your future self that they / you can change this if they / you want.Exclude<T, U>should only remove types fromTthat it can actually find inU. For example:Exclude<string|number|boolean|function, number|boolean>should give the typestring|functionExclude<string|number|boolean, function|userdata>should give the typestring|number|boolean, as neitherfunctionnoruserdataare inTImplementation
These types would most likely require implementing some features from TypeScript:
predicate ? T : U, butif predicate then T else Uwould probably fit in better with Lua, and potentially allow forelseifdown the line if deemed to be necessary or just a good addition; plus, LuaCATS already uses:as the inheritance-operator and?as the optional/nilable-operator). Examples:[string, number, boolean]could be accessed with[string, number, boolean][1]bizof the functionfun(foo, bar, biz, baz)could be retrieved withKeys<FunctionParameters<fun(foo, bar, biz, baz)>>[3]bizfield of the table{foo: "bar", biz: "baz"}could be accessed with{foo: "bar", biz: "baz"}["biz"]Alternatively, instead of fully implementing these features, it might be enough to "simply" hard-code types like
KeysandValues. It might also be a good idea to investigate allowing to write types in Lua (perhaps via plug-ins) instead.If these are not implemented via hard-coding, the following keywords from TypeScript would most likely have to be implemented as well
in(for collection-like types)keyofOnce that's all done, some of them could be the implemented like this:
Considerations
I am decently familiar with LPEG grammars, so I could probably help make all of this happen if given some pointers in the right directions.