luau icon indicating copy to clipboard operation
luau copied to clipboard

String literals reduced to generic string type when being cast to intersection type

Open BizzarBlitz opened this issue 2 years ago • 1 comments

Worth noting the Devforum post on the topic, which remains unresolved: https://devforum.roblox.com/t/string-constants-lose-type-info-when-being-cast-to-an-intersection-type/2008679

Repro:

type X = {
    FieldX: "ValueX"
}
type Y = {
    FieldY: "ValueY"
}

local XandY: X & Y = { --(Inferred to be type '{ FieldX: string, FieldY: string }'
    FieldX = "ValueX",
    FieldY = "ValueY"
}
--^^^^^^^^^^^^^^^^^^^^ Type Error

image

Workaround:

Manually typecasting the table fields into their respective literal types

local XandY: X & Y = { 
    FieldX = "ValueX" :: "ValueX",
    FieldY = "ValueY" :: "ValueY"
}

BizzarBlitz avatar Jun 24 '23 16:06 BizzarBlitz

I think the following bug belongs to the same category?

This works:

--!strict
type MyTaggedUnion = VariantA | VariantB
type VariantA = { discriminator: "A", a: number }
type VariantB = { discriminator: "B", b: string }

local _my_tagged_union: MyTaggedUnion = { discriminator = "B", b = "test" }

This gives an error (TypeError: Type '_my_tagged_union' could not be converted into '({| a: number |} & {| discriminator: "A" |}) | ({| b: string |} & {| discriminator: "B" |})'; none of the union options are compatible):

--!strict
type MyTaggedUnion = VariantA | VariantB
type VariantA = { discriminator: "A" } & { a: number }
type VariantB = { discriminator: "B" } & { b: string }

local _my_tagged_union: MyTaggedUnion = { discriminator = "B", b = "test" }

This is unfortunate because as a consequence, reusing existing types within tagged unions is very limited. The following example leads to the same error:

--!strict
type MyTaggedUnion = VariantA | VariantB
type VariantA = { discriminator: "A" } & PayloadA
type VariantB = { discriminator: "B" } & PayloadB
type PayloadA = { a: number }
type PayloadB = { b: string }

local _my_tagged_union: MyTaggedUnion = { discriminator = "B", b = "test" }

helgoboss avatar Jan 23 '24 11:01 helgoboss