luau-lang / luau

A fast, small, safe, gradually typed embeddable scripting language derived from Lua
https://luau-lang.org
MIT License
3.79k stars 349 forks source link

Type 'T' could not be converted into 'T' #1256

Closed unittensor closed 1 month ago

unittensor commented 1 month ago

With the code below in strict mode an error is prompted "Type 'T' could not be converted into 'T'". Im able to replicate this issue multiple times:

Example 1 using T as boolean:

--!strict

type Callback = <T>(Toggled: T) -> ()

local function asd(Callback: Callback)
    Callback(true)
end

local function BoolValue(Value: boolean)
    print(Value)
end

asd(function<boolean>(Toggled: boolean)
    BoolValue(Toggled) --Type 'boolean' could not be converted into 'boolean'
end)

Example 2 using number as T generic:

--!strict

type Callback = <T>(Toggled: T) -> ()

local function asd(Callback: Callback)
    Callback(1)
end

local function BoolValue(Value: number)
    print(Value)
end

asd(function<number>(Toggled: number)
    BoolValue(Toggled) --Type 'number' could not be converted into 'number'
end)

Workarounds? Change the required function argument type of BoolValue to any:

- local function BoolValue(Value: number)
+ local function BoolValue(Value: any)
--!strict

type Callback = <T>(Toggled: T) -> ()

local function asd(Callback: Callback)
    Callback(1)
end

local function BoolValue(Value: any) --πŸ‘ˆ
    print(Value)
end

asd(function<number>(Toggled: number)
    BoolValue(Toggled) --πŸ‘Œ
end)
goldenstein64 commented 1 month ago

In your case, all of these are equivalent:

-- case 1
asd(function<boolean>(Toggled: boolean)
  BoolValue(Toggled)
end)

-- case 2
asd(function<T>(Toggled: T)
  BoolValue(Toggled)
end)

-- case 3
local function callback<T>(Toggled: T)
  BoolValue(Toggled)
end
asd(callback)

This would be a good counterexample:

asd(function<{ x: number }>(Toggled) -- SyntaxError: Expected identifier, got '{'
  print(Toggled)
end)

In case 1, the type in the angle brackets is not referring to the concrete type boolean = true | false, but a new type created by the anonymous function definition. In general, the angle brackets in a function definition define type parameters, they don't refer to existing types.

The best workaround I can think of is moving the type parameter to asd instead of Callback and passing the parameter value to asd directly.

--!strict

type Callback<T> = (Toggled: T) -> ()

local function asd<T>(Callback: Callback<T>, Value: T)
  Callback(Value)
end

local BoolValue: (boolean) -> () = print
local NumberValue: (number) -> () = print

asd(BoolValue, true)
asd(NumberValue, 1)

It would probably be a good idea to "reserve" existing types from type parameters so they can't be confused.

aatxe commented 1 month ago

Since this is user error, I'm going to close out this issue. You can open a new issue if you want to suggest some particular behavior for naming a generic the same thing as a primitive type, but the type error in this case is right.

unittensor commented 1 month ago

Since this is user error, I'm going to close out this issue. You can open a new issue if you want to suggest some particular behavior for naming a generic the same thing as a primitive type, but the type error in this case is right.

Ah! thank you, thats my fault for the misunderstanding