From 75dad7ae3aec39e290a6aafa74d86eee1de9a4b4 Mon Sep 17 00:00:00 2001 From: Ardi Date: Fri, 26 Dec 2025 13:06:45 -0600 Subject: [PATCH 1/3] amend index-type-operator.md to match runtime behavior --- docs/index-type-operator.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/docs/index-type-operator.md b/docs/index-type-operator.md index be634513c..493fddd4e 100644 --- a/docs/index-type-operator.md +++ b/docs/index-type-operator.md @@ -78,14 +78,18 @@ type idxType3 = index -- idxType3 = number | string type idxType4 = index -- Error message: Property '"age" | "alive"' does not exist on type 'Person | Person2' ``` -In the circumstance that the indexee's type is a class or table with an `__index` metamethod, the `__index` metamethod will *only* be invoked if the indexer is not found within the current scope: +In the circumstance that the indexee's type is a class or table with an `__index` metamethod, the type should reflect +the result of the expression `(tableValue & ~nil) | (__indexValue)` where `~` is the negation type symbol. This fits +with Luau's runtime behavior of metatables, falling back to a value from __index if the value is nonpresent. ```luau local exampleClass = { Foo = "eight" } local exampleClass2 = setmetatable({ Foo = 8 }, { __index = exampleClass }) local exampleClass3 = setmetatable({ Bar = "nine" }, { __index = exampleClass }) +local exampleClass4 = setmetatable({ Foo = ... :: number? }, { __index = exampleClass }) type exampleTy2 = index -- exampleTy2 = number type exampleTy3 = index -- exampleTy3 = string +type exampleTy3 = index -- exampleTy4 = number | string ``` One edge case to consider when using/designing this type function is that `__index` only supports 100 nested `__index` metamethods until it gives up. In the case that a property is not found within the 100 recursive calls, this type function will fail to reduce. From 58cc8ca955b3aa20cde8511cf698c39db593c05e Mon Sep 17 00:00:00 2001 From: Ardi Date: Fri, 26 Dec 2025 13:43:45 -0600 Subject: [PATCH 2/3] fix semantics --- docs/index-type-operator.md | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/docs/index-type-operator.md b/docs/index-type-operator.md index 493fddd4e..83642c773 100644 --- a/docs/index-type-operator.md +++ b/docs/index-type-operator.md @@ -78,9 +78,10 @@ type idxType3 = index -- idxType3 = number | string type idxType4 = index -- Error message: Property '"age" | "alive"' does not exist on type 'Person | Person2' ``` -In the circumstance that the indexee's type is a class or table with an `__index` metamethod, the type should reflect -the result of the expression `(tableValue & ~nil) | (__indexValue)` where `~` is the negation type symbol. This fits -with Luau's runtime behavior of metatables, falling back to a value from __index if the value is nonpresent. +In the circumstance that the indexee's type is a class or table with an `__index` metamethod, and the result from the +main table is nilable, the type should reflect the result of the expression `(tableValue & ~nil) | (__indexValue)` where +`~` is the negation type symbol. This fits with Luau's runtime behavior of metatables, falling back to a value from +__index if the main value is nonpresent. ```luau local exampleClass = { Foo = "eight" } local exampleClass2 = setmetatable({ Foo = 8 }, { __index = exampleClass }) From 5e0e80494a70890798156b6d4396d5a1a0279b37 Mon Sep 17 00:00:00 2001 From: Ardi <113623122+hardlyardi@users.noreply.github.com> Date: Fri, 26 Dec 2025 14:37:22 -0600 Subject: [PATCH 3/3] Update docs/index-type-operator.md Co-authored-by: Vyacheslav Egorov --- docs/index-type-operator.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/index-type-operator.md b/docs/index-type-operator.md index 83642c773..bfa5df02d 100644 --- a/docs/index-type-operator.md +++ b/docs/index-type-operator.md @@ -90,7 +90,7 @@ local exampleClass4 = setmetatable({ Foo = ... :: number? }, { __index = example type exampleTy2 = index -- exampleTy2 = number type exampleTy3 = index -- exampleTy3 = string -type exampleTy3 = index -- exampleTy4 = number | string +type exampleTy4 = index -- exampleTy4 = number | string ``` One edge case to consider when using/designing this type function is that `__index` only supports 100 nested `__index` metamethods until it gives up. In the case that a property is not found within the 100 recursive calls, this type function will fail to reduce.