String literals reduced to generic string type when being cast to intersection type
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
Workaround:
Manually typecasting the table fields into their respective literal types
local XandY: X & Y = {
FieldX = "ValueX" :: "ValueX",
FieldY = "ValueY" :: "ValueY"
}
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" }