tl icon indicating copy to clipboard operation
tl copied to clipboard

Integer doesn't behave as a subtype of number when used inside generic signatures - "got integer, expected number"

Open dashdotdashdot opened this issue 6 months ago • 1 comments

The integer is usually accepted where number is required (but not vice-versa), the former being a subtype of the latter per the docs:

local f = function(x: number): number
	return x
end

local g = function(x: integer): integer
	return x
end

local ok = f(g(1)) -- integer correctly accepted as number
local not_ok = g(f(1)) -- number correctly rejected as integer with "got number, expected integer"

But, when used with generics:

local type Identity = record<T>
	v: T
end

local add_id = function(x: Identity<number>, y: Identity<number>): number
	return x.v + y.v
end

local num1: Identity<integer> = {v=1}
local num2: Identity<integer> = {v=2}

local num_sum = add_id(num1, num2) -- integer rejected as number with "type parameter <number>: got integer, expected number"

(Poking at this through type constraints using function<I is Identity<number>>(x: I, y: I): number, which instead throws given type Identity<integer> does not satisfy Identity<number> constraint in type variable I, and function<N is number>(x: Identity<N>, y: Identity<N>): number throws "expected an interface".)

dashdotdashdot avatar May 20 '25 08:05 dashdotdashdot

The problem with allowing it is that one can break the types.

Add a x.v = 2.5 to the add_id function and suddenly the first Identity<integer> you passed to it becomes an actual Identity<number>

passing an integer to a function that expects a number doesn't run into this because the function has no way to overwrite the original value, it only works on a copied version of it after all.

( https://teal-playground.netlify.app/?c=bG9jYWwgdHlwZSBJZGVudGl0eSA9IHJlY29yZDxUPgoJdjogVAplbmQKCmxvY2FsIGFkZF9pZCA9IGZ1bmN0aW9uKHg6IElkZW50aXR5PG51bWJlcj4sIHk6IElkZW50aXR5PG51bWJlcj4pOiBudW1iZXIKCXgudiA9IDIuNQogICAgcmV0dXJuIHgudiArIHkudgplbmQKCmxvY2FsIG51bTE6IElkZW50aXR5PGludGVnZXI%2BID0ge3Y9MX0KbG9jYWwgbnVtMjogSWRlbnRpdHk8aW50ZWdlcj4gPSB7dj0yfQpwcmludChudW0xLnYpCmxvY2FsIG51bV9zdW0gPSBhZGRfaWQobnVtMSBhcyBJZGVudGl0eTxudW1iZXI%2BLCBudW0yIGFzIElkZW50aXR5PG51bWJlcj4pCnByaW50KG51bTEudik%3D )

lenscas avatar May 20 '25 12:05 lenscas