Try this:
function run(data: test)
local func = data.func
if func then
func()
end
end
It’s also more efficient.
Try this:
function run(data: test)
local func = data.func
if func then
func()
end
end
It’s also more efficient.
Although this is a valid workaround, I’d argue that the type checker should still have transient refining of types throughout the flow of a block of code or expression, even if it’s an indexed property of that value.
As a general rule, if the type system makes you re-write how your code functions at runtime even though your code is theoretically safe and valid, it’s the fault of the type system.
Is there a way I can accept only two or three types into a function? like
function test(x: number or boolean)
end
Not sure about the use case of this, just wondering if its possible, and how to do it if so.
function test(x: number | boolean)
end
Ah i see, is that similar to javascript’s || operator?
Also, thank you.
That’s fair. The assumption is whether it’s allowed for __index to have side effects within the type system.
@zeuxcg
Looks like nonstrict mode lets unknown globals slip through the cracks in certain cases, whereas nocheck mode will mark this as a warning:
Emits a warning:
--!nonstrict
local x = {}
function x:y()
self.value = thisVariableDoesNotExist
end
Emits a warning:
--!nocheck
local x: any = {}
function x:y()
self.value = thisVariableDoesNotExist
end
Emits no warning (even though the exact same code in nocheck mode will emit a warning):
--!nonstrict
local x: any = {}
function x:y()
self.value = thisVariableDoesNotExist
end
the “any” type annotation matters here because a lot of functions in nonstrict mode can return a table that is inferred as any
in nonstrict mode, including the results of table.create(...)
.
This is great feature for me because I don’t have to use bunch of assert
command. Is strict mode enabled by default?
There is merit to what you’re saying - I especially take note that external tools would rely on viewing changes in the source to determine if it is strict or not. It feels really hacky to use a comment notation to declare the script as strict typescript… especially for a fully released feature.
I think the best compromise that wouldn’t include changing the LuaSourceContainer API would be to have some non-commented code that declares the script as strict, instead of a comment. For example:
useStrictTypeChecking()
where a useStrictTypeChecking function is declared in the function environment.
Problem with this is that it’ll still introduce new API, only in a more tucked away place. Ultimately, with new features, new interfaces are needed to interact with them. I think adding one new enum for an entirely different coding style would be proper for something like this. I think the main thing though is that when you convert a script to strict typescript, it needs to feel like you’re actually changing something, rather than just adding a typical comment at the top.
The following script results in the warning W000: (2,1) Expected to return 0 values, but 1 is returned here
--!strict
return
But the script below results in no warning
--!strict
return 0
Are there any types that are implicitly nullable, or that will be implicitly nullable?
local part = Instance.new("Part")
part = nil
This raises a warning because the part variable is not of type Part?
, but in my opinion, like objects in other languages, Instance
should be implicitly nullable.
Another one that is mildly irritating is references to tables. In all of my custom classes I offer a Dispose
method that detaches everything from its containing type (by setting it to nil) so that GC can pick it up. For instance:
type MyType = {
Var: {[any]: any}
}
function Dispose(obj: MyType)
obj.Var = nil
end
This will raise a warning since the table is not nullable.
Luau beginning to look like C#, I like this.
The answer is: Yes.
I’ve tried rejoining my game (in studio) but I crashed everytime.
Is it not possible to have mixed type ordered tuples?
A tuple is a fixed length ordered array, I often use them when storing two or three related values.
Here is an example of all the syntaxes I tried and the errors they produce:
--!strict
-- Syntax error: Expected ':' when parsing table field, got ','
type ConnectionArgument = { string, string, boolean }
-- Syntax error: Expected type, got '1'
type ConnectionArgument = { 1: string, 2: string, 3: boolean }
-- Syntax error: Expected type, got '1'
type ConnectionArgument = { [1]: string, [2]: string, [3]: boolean }
-- W000: Type 'boolean' could not be converted into 'string'
type ConnectionArgument = typeof({ '', '', false })
-- W000: Type 'boolean' could not be converted into 'string'
local argument: ConnectionArgument = { 'number', 'ArgOne', false }
-- This is how I would later use the tuple, all three values always used together
local class, name, optional = unpack(argument)
An interesting problem with this is that the index signature syntax (i.e. {[string]: any}
) directly conflicts with the syntax of using literal types as keys in an interface. Would {[1]: any}
mean that key ‘1’ must have a value of “any” type, or does it mean that all keys must have a 1
type, and all values have an any
type?
Granted, literal types aren’t supported yet, but I’d imagine that they should be down the road.
Found a second issue, using assert will result in type refinement as expected.
But when I assert a property to be nil it will now not allow anything to be assigned to that property.
I commonly use this to catch errors for methods which should only be called once per instance.
A third issue is that there is no syntax which allows vararg function types.
I would like ConnectionHandler to be a vararg which always has the first argument as type Player.
Using ...
in the type define will result in an error, and so will trying to add extra args to handler
Here is some example code and the error produced:
Network.AssertClass is a custom function which raises an error if param one is not of type param two.
-- If ', ...' is added: Syntax error: Expected type, got '...'
export type ConnectionHandler = (Player) -> ()
export type Connection = {
--[[ Unrelated fields omited ]]
Callback: ConnectionHandler?
}
--- Add a callback to this connection, called when a client calls fetch
function Connection:Register(callback: ConnectionHandler)
assert(self.Callback == nil, 'Connection already has a callback registered')
Network.AssertClass(callback, 'function', 'Argument #1 to Register')
--W000: Type '(Player) -> ()' could not be converted into 'nil'
self.Callback = callback
end
-- Example handler that might be passed to Connection:Register
-- W000: Type '(Player, any, any) -> ()' could not be converted into '(Player) -> ()'
local handler: ConnectionHandler = function(player: Player, argOne: any, argTwo: any)
print(player.UserId)
end
local str = "a"
print(
(
str == "a" and "a"
or str == "b" and "b"
or "z"
).."c"
)
results in W000: (173,9) expected a string or number, got boolean | string
I think there should be a button in Script Analysis that will hide selected type warnings and make them dark grey somewhere in hidden warnings folder and won’t underline them because there is so many problems with this checker that sometimes there is no workaround other than using –!nocheck but that is not the best solution too after all. I think if you could somehow hide them with ability to show them again it would make it a lot better, something like in planes when there is master caution, pilots
click the button and it stops flashing.
Also there are issues with remote events and bindable events where you can’t have more than 1 argument in Fire or FireServer/Client, it will throw warning like this
Good stuff, that’s an issue I ran into quite often! Thanks, and keep up the good work!
It looks like a recent update caused a regression where do end
blocks can make output extremely verbose and unreadable in some cases for strict mode:
Simplified Repro:
Here I have defined a decently-sized interface type, and a variable x
being assigned to it, except that I have an extraneous field, which should output a warning.
--!strict
type DefinedType = {
foo: string,
fighters: string,
dave: string,
pat: string,
nate: string,
chris: string,
taylor: string,
rami: string
}
local x: DefinedType = {
extraneousField = 'This is extraneous',
foo = 'Hello',
fighters = '',
dave = '',
pat = 'string',
nate = 'string',
chris = 'string',
taylor = 'string',
rami = 'string'
}
The warning here is extremely readable and easy to diagnose the issue:
However, if I scope the last statement in a do end
block, it all the sudden makes the output much more verbose and less readable:
--!strict
type DefinedType = {
foo: string,
fighters: string,
dave: string,
pat: string,
nate: string,
chris: string,
taylor: string,
rami: string
}
do
local x: DefinedType = {
extraneousField = 'This is extraneous',
foo = 'Hello',
fighters = '',
dave = '',
pat = 'string',
nate = 'string',
chris = 'string',
taylor = 'string',
rami = 'string'
}
end
When working with code that has more complex types, this becomes completely unmanageable and impossible to diagnose the issue aside from trial and error, guessing where things are going wrong; the warning won’t even fit on the screen with my 1440p monitor!