@rbxts/libopen-ty 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE.md ADDED
@@ -0,0 +1,16 @@
1
+ Permission is hereby granted, free of charge, to any person obtaining a copy of
2
+ this software and associated documentation files (the “Software”), to deal in
3
+ the Software without restriction, including without limitation the rights to
4
+ use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
5
+ the Software, and to permit persons to whom the Software is furnished to do so,
6
+ subject to the following conditions:
7
+
8
+ The above copyright notice and this permission notice shall be included in all
9
+ copies or substantial portions of the Software.
10
+
11
+ THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
12
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
13
+ FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
14
+ COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
15
+ IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
16
+ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,12 @@
1
+ # rbxts-elttob-libopen
2
+
3
+ roblox-ts typings for [Studio's Elttob LibOpen](https://github.com/elttob/LibOpen).
4
+
5
+ I, znotfireman, am not affiliated, associated, authorized, endorsed by, or in
6
+ any way officially connected with Studio Elttob.
7
+
8
+ ## Excluded packages
9
+
10
+ - `LibOpen/Fusion` — please use my `@rbxts/hotfusion` or
11
+ `@rbxts/fusion-temp-0.3` aliased with `@rbxts/fusion`, both packages have
12
+ built-in JSX support.
@@ -0,0 +1,6 @@
1
+ {
2
+ "name": "ty",
3
+ "tree": {
4
+ "$path": "src"
5
+ }
6
+ }
package/package.json ADDED
@@ -0,0 +1,22 @@
1
+ {
2
+ "name": "@rbxts/libopen-ty",
3
+ "version": "1.0.0",
4
+ "description": "LibOpen's ty",
5
+ "main": "src/init.luau",
6
+ "license": "MIT",
7
+ "types": "src/index.d.ts",
8
+ "files": [
9
+ "src",
10
+ "LICENSE.md",
11
+ "default.project.json",
12
+ "package.json",
13
+ "!**/*.tsbuildinfo"
14
+ ],
15
+ "publishConfig": {
16
+ "access": "public"
17
+ },
18
+ "dependencies": {
19
+ "@rbxts/libopen-maybe": "npm:@rbxts/libopen-ty@1.0.0"
20
+ },
21
+ "scripts": {}
22
+ }
package/src/Def.luau ADDED
@@ -0,0 +1,33 @@
1
+ --!strict
2
+ -- ty (Elttob Suite monorepo edition - no static types)
3
+ -- By Daniel P H Fox, licensed under MIT
4
+
5
+ local Package = script.Parent.Parent
6
+ local Types = require(Package.ty.Types)
7
+ local Maybe = require(Package.Maybe)
8
+
9
+ local Def = {}
10
+
11
+ Def.methodMeta = {__index = nil :: Types.Methods?}
12
+
13
+ local function linkMethods(
14
+ def
15
+ ): Types.Def
16
+ setmetatable(def, Def.methodMeta)
17
+ return def :: any
18
+ end
19
+
20
+ function Def.new<CastTo>(
21
+ expectsType: string,
22
+ matches: (self: Types.Def, unknown) -> boolean,
23
+ cast: (self: Types.Def, unknown) -> Maybe.Maybe<any>
24
+ ): Types.Def
25
+ local def = {}
26
+ def.Matches = matches
27
+ def.ExpectsType = expectsType
28
+ def.NotOfTypeError = `Type is not {expectsType}`
29
+ def.Cast = cast
30
+ return linkMethods(def)
31
+ end
32
+
33
+ return Def
@@ -0,0 +1,32 @@
1
+ --!strict
2
+ --!nolint LocalShadow
3
+ -- ty (Elttob Suite monorepo edition - no static types)
4
+ -- By Daniel P H Fox, licensed under MIT
5
+
6
+ local Package = script.Parent.Parent.Parent
7
+ local Maybe = require(Package.Maybe)
8
+
9
+ local Types = require(Package.ty.Types)
10
+ local Def = require(Package.ty.Def)
11
+
12
+ local And: Types.FnAnd = function(first, last)
13
+ return Def.new(
14
+ `({first.ExpectsType}) & ({last.ExpectsType})`,
15
+ function(self, x)
16
+ return first:Matches(x) and last:Matches(x)
17
+ end,
18
+ function(self, x)
19
+ local result = first:Cast(x)
20
+ if not result.some then
21
+ return Maybe.None(`{self.NotOfTypeError}\n...because of the first & type: {result.reason}`)
22
+ end
23
+ local result = last:Cast(result.value)
24
+ if not result.some then
25
+ return Maybe.None(`{self.NotOfTypeError}\n...because of the last & type: {result.reason}`)
26
+ end
27
+ return result
28
+ end
29
+ )
30
+ end
31
+
32
+ return And
@@ -0,0 +1,57 @@
1
+ --!strict
2
+ --!nolint LocalShadow
3
+ -- ty (Elttob Suite monorepo edition - no static types)
4
+ -- By Daniel P H Fox, licensed under MIT
5
+
6
+ local Package = script.Parent.Parent.Parent
7
+ local Maybe = require(Package.Maybe)
8
+
9
+ local Types = require(Package.ty.Types)
10
+ local Def = require(Package.ty.Def)
11
+
12
+ -- TODO: ensure arrays have no holes, and empty arrays are allowed
13
+ local Array: Types.FnArray = function<V>(values: Types.Def)
14
+ return Def.new(
15
+ `\{{values.ExpectsType}}`,
16
+ function(self, x)
17
+ if typeof(x) ~= "table" then
18
+ return false
19
+ end
20
+ local x = x :: {[unknown]: unknown}
21
+ local expected = #x
22
+ local encountered = 0
23
+ for key, value in pairs(x) do
24
+ encountered += 1
25
+ if encountered > expected or typeof(key) ~= "number" or not values:Matches(value) then
26
+ return false
27
+ end
28
+ end
29
+ return true
30
+ end,
31
+ function(self, x)
32
+ if typeof(x) ~= "table" then
33
+ return Maybe.None(`{self.NotOfTypeError}\n...because {tostring(x)} isn't an array`)
34
+ end
35
+ local x = x :: {[unknown]: unknown}
36
+ local modifiedReturn = {}
37
+ local expected = #x
38
+ local encountered = 0
39
+ for key, value in pairs(x) do
40
+ encountered += 1
41
+ if encountered > expected or typeof(key) ~= "number" then
42
+ return Maybe.None(`{self.NotOfTypeError}\n...because {tostring(key)} isn't a valid array index`)
43
+ end
44
+ local castedValue = values:Cast(value)
45
+ if not castedValue.some then
46
+ return Maybe.None(`{self.NotOfTypeError}\n...because, at key {tostring(key)}: {castedValue.reason}`)
47
+ end
48
+ if castedValue ~= nil then
49
+ table.insert(modifiedReturn, castedValue.value :: any)
50
+ end
51
+ end
52
+ return Maybe.Some(modifiedReturn)
53
+ end
54
+ )
55
+ end
56
+
57
+ return Array
@@ -0,0 +1,17 @@
1
+ --!strict
2
+ -- ty (Elttob Suite monorepo edition - no static types)
3
+ -- By Daniel P H Fox, licensed under MIT
4
+
5
+ local Package = script.Parent.Parent.Parent
6
+ local Types = require(Package.ty.Types)
7
+
8
+ local CastOrError: Types.FnCastOrError = function(def, x)
9
+ local result = def:Cast(x)
10
+ if result.some then
11
+ return result.value
12
+ else
13
+ error(`Could not cast {typeof(x)}: {result.reason}`)
14
+ end
15
+ end
16
+
17
+ return CastOrError
@@ -0,0 +1,25 @@
1
+ --!strict
2
+ --!nolint LocalShadow
3
+ -- ty (Elttob Suite monorepo edition - no static types)
4
+ -- By Daniel P H Fox, licensed under MIT
5
+
6
+ local Package = script.Parent.Parent.Parent
7
+ local Maybe = require(Package.Maybe)
8
+
9
+ local Types = require(Package.ty.Types)
10
+ local Def = require(Package.ty.Def)
11
+
12
+ local IgnoreInvalid: Types.FnIgnoreInvalid = function<T>(innerDef: Types.Def)
13
+ return Def.new(
14
+ `@ignoreInvalid({innerDef.ExpectsType})`,
15
+ function(self, x)
16
+ return true
17
+ end,
18
+ function(self, x)
19
+ local result = innerDef:Cast(x)
20
+ return if result.some then result else Maybe.Some(nil)
21
+ end
22
+ )
23
+ end
24
+
25
+ return IgnoreInvalid
@@ -0,0 +1,28 @@
1
+ --!strict
2
+ --!nolint LocalShadow
3
+ -- ty (Elttob Suite monorepo edition - no static types)
4
+ -- By Daniel P H Fox, licensed under MIT
5
+
6
+ local Package = script.Parent.Parent.Parent
7
+ local Maybe = require(Package.Maybe)
8
+
9
+ local Types = require(Package.ty.Types)
10
+ local Def = require(Package.ty.Def)
11
+
12
+ local IntoDefault: Types.FnIntoDefault = function(innerDef, defaultValue)
13
+ return Def.new(
14
+ `\@default<{defaultValue}>({innerDef.ExpectsType})`,
15
+ function(self, x)
16
+ return x == nil or innerDef:Matches(x)
17
+ end,
18
+ function(self, x)
19
+ if x == nil then
20
+ return Maybe.Some(defaultValue)
21
+ else
22
+ return innerDef:Cast(x)
23
+ end
24
+ end
25
+ )
26
+ end
27
+
28
+ return IntoDefault
@@ -0,0 +1,35 @@
1
+ --!strict
2
+ --!nolint LocalShadow
3
+ -- ty (Elttob Suite monorepo edition - no static types)
4
+ -- By Daniel P H Fox, licensed under MIT
5
+
6
+ local Package = script.Parent.Parent.Parent
7
+ local Maybe = require(Package.Maybe)
8
+
9
+ local Types = require(Package.ty.Types)
10
+ local Def = require(Package.ty.Def)
11
+
12
+ local IntoNumber: Types.FnIntoNumber = function(innerDef, base)
13
+ return Def.new(
14
+ `@tonumber({innerDef.ExpectsType}{if base == nil then "" else ", " .. base})`,
15
+ function(self, x)
16
+ local Cast = innerDef:Cast(x)
17
+ return Cast.some and tonumber(Cast.value, base) ~= nil
18
+ end,
19
+ function(self, x)
20
+ local result = innerDef:Cast(x)
21
+ if not result.some then
22
+ return Maybe.None(`{self.NotOfTypeError}\n...because of the inner type: {result.reason}`)
23
+ else
24
+ local num = tonumber(result.value, base)
25
+ if num == nil then
26
+ return Maybe.None(`Numeric conversion not possible for {self.ExpectsType}`)
27
+ else
28
+ return Maybe.Some(num)
29
+ end
30
+ end
31
+ end
32
+ )
33
+ end
34
+
35
+ return IntoNumber
@@ -0,0 +1,35 @@
1
+ --!strict
2
+ --!nolint LocalShadow
3
+ -- ty (Elttob Suite monorepo edition - no static types)
4
+ -- By Daniel P H Fox, licensed under MIT
5
+
6
+ local Package = script.Parent.Parent.Parent
7
+ local Maybe = require(Package.Maybe)
8
+
9
+ local Types = require(Package.ty.Types)
10
+ local Def = require(Package.ty.Def)
11
+
12
+ local IntoString: Types.FnIntoString = function(innerDef)
13
+ return Def.new(
14
+ `@tostring({innerDef.ExpectsType})`,
15
+ function(self, x)
16
+ local Cast = innerDef:Cast(x)
17
+ return Cast.some and tostring(Cast.value) ~= nil
18
+ end,
19
+ function(self, x)
20
+ local result = innerDef:Cast(x)
21
+ if not result.some then
22
+ return Maybe.None(`{self.NotOfTypeError}\n...because of the inner type: {result.reason}`)
23
+ else
24
+ local str = tostring(result.value)
25
+ if str == nil then
26
+ return Maybe.None(`{self.NotOfTypeError}\n...because it couldn't be turned into a string`)
27
+ else
28
+ return Maybe.Some(str)
29
+ end
30
+ end
31
+ end
32
+ )
33
+ end
34
+
35
+ return IntoString
@@ -0,0 +1,34 @@
1
+ --!strict
2
+ --!nolint LocalShadow
3
+ -- ty (Elttob Suite monorepo edition - no static types)
4
+ -- By Daniel P H Fox, licensed under MIT
5
+
6
+ local Package = script.Parent.Parent.Parent
7
+ local Maybe = require(Package.Maybe)
8
+
9
+ local Types = require(Package.ty.Types)
10
+ local Def = require(Package.ty.Def)
11
+
12
+ local IntoTagged: Types.FnIntoTagged = function(innerDef, tag)
13
+ return Def.new(
14
+ `\@tagged<{tag}>({innerDef.ExpectsType})`,
15
+ function(self, x)
16
+ return innerDef:Matches(x)
17
+ end,
18
+ function(self, x)
19
+ local result = innerDef:Cast(x)
20
+ if not result.some then
21
+ return Maybe.None(`{self.NotOfTypeError}\n...because of the inner type: {result.reason}`)
22
+ else
23
+ local value =
24
+ if typeof(result.value) == "table"
25
+ then table.clone(result.value :: any) :: any
26
+ else {value = result.value}
27
+ value.__tag = tag
28
+ return Maybe.Some(value)
29
+ end
30
+ end
31
+ )
32
+ end
33
+
34
+ return IntoTagged
@@ -0,0 +1,28 @@
1
+ --!strict
2
+ --!nolint LocalShadow
3
+ -- ty (Elttob Suite monorepo edition - no static types)
4
+ -- By Daniel P H Fox, licensed under MIT
5
+
6
+ local Package = script.Parent.Parent.Parent
7
+ local Maybe = require(Package.Maybe)
8
+
9
+ local Types = require(Package.ty.Types)
10
+ local Def = require(Package.ty.Def)
11
+
12
+ local Just: Types.FnJust = function(literal, type)
13
+ return Def.new(
14
+ type or tostring(literal),
15
+ function(self, x)
16
+ return rawequal(x, literal)
17
+ end,
18
+ function(self, x)
19
+ if rawequal(x, literal) then
20
+ return Maybe.Some(x)
21
+ else
22
+ return Maybe.None(`{self.NotOfTypeError}\n...because only {tostring(literal)} is accepted, but received {tostring(x)}`)
23
+ end
24
+ end
25
+ )
26
+ end
27
+
28
+ return Just
@@ -0,0 +1,49 @@
1
+ --!strict
2
+ --!nolint LocalShadow
3
+ -- ty (Elttob Suite monorepo edition - no static types)
4
+ -- By Daniel P H Fox, licensed under MIT
5
+
6
+ local Package = script.Parent.Parent.Parent
7
+ local Maybe = require(Package.Maybe)
8
+
9
+ local Types = require(Package.ty.Types)
10
+ local Def = require(Package.ty.Def)
11
+
12
+ local MapOf: Types.FnMapOf = function<K, V>(keys: Types.Def, values: Types.Def)
13
+ return Def.new(
14
+ `\{[{keys.ExpectsType}]: {values.ExpectsType}}`,
15
+ function(self, x)
16
+ if typeof(x) ~= "table" then
17
+ return false
18
+ end
19
+ local x = x :: {[unknown]: unknown}
20
+ for key, value in pairs(x) do
21
+ if not keys:Matches(key) or not values:Matches(value) then
22
+ return false
23
+ end
24
+ end
25
+ return true
26
+ end,
27
+ function(self, x)
28
+ if typeof(x) ~= "table" then
29
+ return Maybe.None(`{self.NotOfTypeError}\n...because {tostring(x)} isn't a table`)
30
+ end
31
+ local x = x :: {[unknown]: unknown}
32
+ local modifiedReturn = {}
33
+ for key, value in pairs(x) do
34
+ local castedKey = keys:Cast(key)
35
+ if not castedKey.some then
36
+ return Maybe.None(`{self.NotOfTypeError}\n...because of the key {tostring(key)}: {castedKey.reason}`)
37
+ end
38
+ local castedValue = values:Cast(value)
39
+ if not castedValue.some then
40
+ return Maybe.None(`{self.NotOfTypeError}\n...because of the value at key {tostring(key)}: {castedValue.reason}`)
41
+ end
42
+ modifiedReturn[castedKey.value :: any] = castedValue.value :: any
43
+ end
44
+ return Maybe.Some(modifiedReturn)
45
+ end
46
+ )
47
+ end
48
+
49
+ return MapOf
@@ -0,0 +1,18 @@
1
+ --!strict
2
+ --!nolint LocalShadow
3
+ -- ty (Elttob Suite monorepo edition - no static types)
4
+ -- By Daniel P H Fox, licensed under MIT
5
+
6
+ local Package = script.Parent.Parent.Parent
7
+ local Types = require(Package.ty.Types)
8
+ local Def = require(Package.ty.Def)
9
+
10
+ local Nicknamed: Types.FnNicknamed = function<T>(innerDef, newName)
11
+ return Def.new(
12
+ newName,
13
+ innerDef.Matches,
14
+ innerDef.Cast
15
+ )
16
+ end
17
+
18
+ return Nicknamed
@@ -0,0 +1,31 @@
1
+ --!strict
2
+ --!nolint LocalShadow
3
+ -- ty (Elttob Suite monorepo edition - no static types)
4
+ -- By Daniel P H Fox, licensed under MIT
5
+
6
+ local Package = script.Parent.Parent.Parent
7
+ local Maybe = require(Package.Maybe)
8
+
9
+ local Types = require(Package.ty.Types)
10
+ local Def = require(Package.ty.Def)
11
+
12
+ local Optional: Types.FnOptional = function<T>(innerDef: Types.Def)
13
+ return Def.new(
14
+ `({innerDef.ExpectsType})?`,
15
+ function(self, x)
16
+ if x == nil then
17
+ return true
18
+ end
19
+ return innerDef:Matches(x)
20
+ end,
21
+ function(self, x)
22
+ if x == nil then
23
+ return Maybe.Some(nil) :: Maybe.Maybe<T?>
24
+ end
25
+ local result = innerDef:Cast(x)
26
+ return if result.some then result else Maybe.None(`{self.NotOfTypeError}\n...because of the inner type: {result.reason}`)
27
+ end
28
+ )
29
+ end
30
+
31
+ return Optional
@@ -0,0 +1,29 @@
1
+ --!strict
2
+ --!nolint LocalShadow
3
+ -- ty (Elttob Suite monorepo edition - no static types)
4
+ -- By Daniel P H Fox, licensed under MIT
5
+
6
+ local Package = script.Parent.Parent.Parent
7
+ local Maybe = require(Package.Maybe)
8
+
9
+ local Types = require(Package.ty.Types)
10
+ local Def = require(Package.ty.Def)
11
+
12
+ local Or: Types.FnOr = function<F, L>(first: Types.Def, last: Types.Def)
13
+ return Def.new(
14
+ `{first.ExpectsType} | {last.ExpectsType}`,
15
+ function(self, x)
16
+ return first:Matches(x) or last:Matches(x)
17
+ end,
18
+ function(self, x)
19
+ local firstResult = first:Cast(x)
20
+ if firstResult.some then
21
+ return (firstResult :: any) :: Maybe.Maybe<F | L>
22
+ end
23
+ local lastResult = last:Cast(x)
24
+ return if lastResult.some then lastResult else Maybe.None(`{self.NotOfTypeError}\n...because neither inner type fits:\nFirst type: {firstResult.reason}\nSecond type: {lastResult.reason}`)
25
+ end
26
+ )
27
+ end
28
+
29
+ return Or
@@ -0,0 +1,28 @@
1
+ --!strict
2
+ --!nolint LocalShadow
3
+ -- ty (Elttob Suite monorepo edition - no static types)
4
+ -- By Daniel P H Fox, licensed under MIT
5
+
6
+ local Package = script.Parent.Parent.Parent
7
+ local Maybe = require(Package.Maybe)
8
+
9
+ local Types = require(Package.ty.Types)
10
+ local Def = require(Package.ty.Def)
11
+
12
+ local Predicate: Types.FnPredicate = function(predicate)
13
+ return Def.new(
14
+ `@predicate`,
15
+ function(self, x)
16
+ return predicate(x)
17
+ end,
18
+ function(self, x)
19
+ if predicate(x) then
20
+ return Maybe.Some(x)
21
+ else
22
+ return Maybe.None(`Type did not satisfy predicate`)
23
+ end
24
+ end
25
+ )
26
+ end
27
+
28
+ return Predicate
@@ -0,0 +1,13 @@
1
+ --!strict
2
+ --!nolint LocalShadow
3
+ -- ty (Elttob Suite monorepo edition - no static types)
4
+ -- By Daniel P H Fox, licensed under MIT
5
+
6
+ local Package = script.Parent.Parent.Parent
7
+ local Types = require(Package.ty.Types)
8
+
9
+ local Retype: Types.FnRetype = function(def)
10
+ return def
11
+ end
12
+
13
+ return Retype
@@ -0,0 +1,62 @@
1
+ --!strict
2
+ --!nolint LocalShadow
3
+ -- ty (Elttob Suite monorepo edition - no static types)
4
+ -- By Daniel P H Fox, licensed under MIT
5
+
6
+ local Package = script.Parent.Parent.Parent
7
+ local Maybe = require(Package.Maybe)
8
+
9
+ local Types = require(Package.ty.Types)
10
+ local Def = require(Package.ty.Def)
11
+
12
+ local Struct: Types.FnStruct = function<K, V>(options, struct: {[K & string]: Types.Def})
13
+ local expectTypeParts = {}
14
+ for key, value in struct do
15
+ table.insert(expectTypeParts, `{key}: {value.ExpectsType}`)
16
+ end
17
+ return Def.new(
18
+ `\{{table.concat(expectTypeParts, ", ")}}`,
19
+ function(self, x)
20
+ if typeof(x) ~= "table" then
21
+ return false
22
+ end
23
+ local x = x :: {[unknown]: unknown}
24
+ for key, value in pairs(x) do
25
+ if typeof(key) ~= "string" then
26
+ return false
27
+ end
28
+ local key = key :: K & string
29
+ local innerDef = struct[key]
30
+ if innerDef == nil or not innerDef:Matches(value) then
31
+ return false
32
+ end
33
+ end
34
+ return true
35
+ end,
36
+ function(self, x)
37
+ if typeof(x) ~= "table" then
38
+ return Maybe.None(`{self.NotOfTypeError}\n...because {tostring(x)} isn't a table`)
39
+ end
40
+ local x = x :: {[unknown]: unknown}
41
+ local modifiedReturn = {}
42
+ for key, innerDef in pairs(struct) do
43
+ local value = x[key]
44
+ local castedValue = innerDef:Cast(value)
45
+ if not castedValue.some then
46
+ return Maybe.None(`{self.NotOfTypeError}\n...because of the value at key {tostring(key)}: {castedValue.reason}`)
47
+ end
48
+ modifiedReturn[key] = castedValue.value :: any
49
+ end
50
+ for key in pairs(x) do
51
+ if typeof(key) ~= "string" then
52
+ return Maybe.None(`{self.NotOfTypeError}\n...because {tostring(key)} isn't a valid struct key`)
53
+ elseif options.exhaustive and struct[key :: any] == nil then
54
+ return Maybe.None(`{self.NotOfTypeError}\n...because the struct is exhaustive and {tostring(key)} isn't included`)
55
+ end
56
+ end
57
+ return Maybe.Some(modifiedReturn)
58
+ end
59
+ )
60
+ end
61
+
62
+ return Struct
@@ -0,0 +1,63 @@
1
+ --!strict
2
+ --!nolint LocalShadow
3
+ -- ty (Elttob Suite monorepo edition - no static types)
4
+ -- By Daniel P H Fox, licensed under MIT
5
+
6
+ local Package = script.Parent.Parent.Parent
7
+ local Maybe = require(Package.Maybe)
8
+
9
+ local Types = require(Package.ty.Types)
10
+ local Def = require(Package.ty.Def)
11
+
12
+ local Tuple: Types.FnTuple = function<V>(options, tuple)
13
+ local expectTypeParts = {}
14
+ for key, value in tuple do
15
+ assert(typeof(key) == "number", "Tuple requires numeric arguments")
16
+ table.insert(expectTypeParts, `[{key}]: {value.ExpectsType}`)
17
+ end
18
+ return Def.new(
19
+ `\{{table.concat(expectTypeParts, ", ")}}`,
20
+ function(self, x)
21
+ if typeof(x) ~= "table" then
22
+ return false
23
+ end
24
+ local x = x :: {[unknown]: unknown}
25
+ for key, value in pairs(x) do
26
+ if typeof(key) ~= "number" then
27
+ return false
28
+ end
29
+ local key = key :: number
30
+ local innerDef = tuple[key]
31
+ if innerDef == nil or not innerDef:Matches(value) then
32
+ return false
33
+ end
34
+ end
35
+ return true
36
+ end,
37
+ function(self, x)
38
+ if typeof(x) ~= "table" then
39
+ return Maybe.None(`{self.NotOfTypeError}\n...because {tostring(x)} isn't a table`)
40
+ end
41
+ local x = x :: {[unknown]: unknown}
42
+ local modifiedReturn = {}
43
+ for key, innerDef in pairs(tuple) do
44
+ local value = x[key]
45
+ local castedValue = innerDef:Cast(value)
46
+ if not castedValue.some then
47
+ return Maybe.None(`{self.NotOfTypeError}\n...because of the value at key {tostring(key)}: {castedValue.reason}`)
48
+ end
49
+ modifiedReturn[key] = castedValue.value :: any
50
+ end
51
+ for key in pairs(x) do
52
+ if typeof(key) ~= "number" then
53
+ return Maybe.None(`{self.NotOfTypeError}\n...because {tostring(key)} isn't a valid tuple key`)
54
+ elseif options.exhaustive and tuple[key] == nil then
55
+ return Maybe.None(`{self.NotOfTypeError}\n...because the tuple is exhaustive and {tostring(key)} isn't included`)
56
+ end
57
+ end
58
+ return Maybe.Some(modifiedReturn)
59
+ end
60
+ )
61
+ end
62
+
63
+ return Tuple
@@ -0,0 +1,28 @@
1
+ --!strict
2
+ --!nolint LocalShadow
3
+ -- ty (Elttob Suite monorepo edition - no static types)
4
+ -- By Daniel P H Fox, licensed under MIT
5
+
6
+ local Package = script.Parent.Parent.Parent
7
+ local Maybe = require(Package.Maybe)
8
+
9
+ local Types = require(Package.ty.Types)
10
+ local Def = require(Package.ty.Def)
11
+
12
+ local Typeof: Types.FnTypeof = function(typeString)
13
+ return Def.new(
14
+ typeString,
15
+ function(self, x)
16
+ return typeof(x) == typeString
17
+ end,
18
+ function(self, x)
19
+ if typeof(x) == typeString then
20
+ return Maybe.Some(x)
21
+ else
22
+ return Maybe.None(`{self.NotOfTypeError}\n...because {tostring(x)} doesn't have typeof() == {typeString}`)
23
+ end
24
+ end
25
+ )
26
+ end
27
+
28
+ return Typeof
@@ -0,0 +1,13 @@
1
+ --!strict
2
+ --!nolint LocalShadow
3
+ -- ty (Elttob Suite monorepo edition - no static types)
4
+ -- By Daniel P H Fox, licensed under MIT
5
+
6
+ local Package = script.Parent.Parent.Parent
7
+ local Types = require(Package.ty.Types)
8
+
9
+ local Untyped: Types.FnUntyped = function(def)
10
+ return def
11
+ end
12
+
13
+ return Untyped
package/src/Types.luau ADDED
@@ -0,0 +1,156 @@
1
+ --!strict
2
+ -- From 'ty' by Elttob, MIT license
3
+
4
+ local Package = script.Parent.Parent
5
+ local Maybe = require(Package.Maybe)
6
+
7
+ export type Def = DefNoMethods & Methods
8
+ type DefNoMethods = {
9
+ ExpectsType: string,
10
+ NotOfTypeError: string,
11
+ Matches: (self: Def, unknown) -> boolean,
12
+ Cast: (self: Def, unknown) -> Maybe.Maybe<unknown>
13
+ }
14
+
15
+ export type Constants = {
16
+ Unknown: Def,
17
+ Never: Def,
18
+
19
+ Number: Def,
20
+ Boolean: Def,
21
+ String: Def,
22
+ Thread: Def,
23
+ Table: Def,
24
+ Function: Def,
25
+
26
+ True: Def,
27
+ False: Def,
28
+ Nil: Def
29
+ }
30
+
31
+ export type Methods = {
32
+ Predicate: FnPredicate,
33
+ Typeof: FnTypeof,
34
+ Just: FnJust,
35
+ Optional: FnOptional,
36
+ IgnoreInvalid: FnIgnoreInvalid,
37
+ Or: FnOr,
38
+ And: FnAnd,
39
+ MapOf: FnMapOf,
40
+ Array: FnArray,
41
+ Struct: FnStruct,
42
+ Tuple: FnTuple,
43
+ IntoTagged: FnIntoTagged,
44
+ IntoString: FnIntoString,
45
+ IntoNumber: FnIntoNumber,
46
+ IntoDefault: FnIntoDefault,
47
+ Retype: FnRetype,
48
+ Untyped: FnUntyped,
49
+ Nicknamed: FnNicknamed,
50
+ CastOrError: FnCastOrError
51
+ }
52
+
53
+ export type FnPredicate = (
54
+ predicate: (unknown) -> boolean
55
+ ) -> Def
56
+
57
+ export type FnTypeof = (
58
+ typeString: string
59
+ ) -> Def
60
+
61
+ export type FnJust = (
62
+ literal: unknown,
63
+ type: string?
64
+ ) -> Def
65
+
66
+ export type Optional = Def
67
+ export type FnOptional = (
68
+ innerDef: Def
69
+ ) -> Optional
70
+
71
+ export type IgnoreInvalid = Def
72
+ export type FnIgnoreInvalid = (
73
+ innerDef: Def
74
+ ) -> IgnoreInvalid
75
+
76
+ export type Or = Def
77
+ export type FnOr = (
78
+ first: Def,
79
+ last: Def
80
+ ) -> Or
81
+
82
+
83
+ export type And = Def
84
+ export type FnAnd = (
85
+ first: Def,
86
+ last: Def
87
+ ) -> And
88
+
89
+ export type MapOf = Def
90
+ export type FnMapOf = (
91
+ keys: Def,
92
+ values: Def
93
+ ) -> MapOf
94
+
95
+
96
+ export type Array = Def
97
+ export type FnArray = <V>(
98
+ values: Def
99
+ ) -> Array
100
+
101
+ -- FUTURE: suboptimal type for this; try keyof when it launches?
102
+ export type Struct = Def
103
+ export type FnStruct = (
104
+ options: {
105
+ exhaustive: boolean
106
+ },
107
+ object: {[string]: Def}
108
+ ) -> Struct
109
+
110
+ export type Tuple = Def
111
+ export type FnTuple = (
112
+ options: {
113
+ exhaustive: boolean
114
+ },
115
+ tuple: {[number]: Def}
116
+ ) -> Tuple
117
+
118
+ export type IntoTagged = Def
119
+ export type FnIntoTagged = (
120
+ innerDef: Def,
121
+ tag: unknown
122
+ ) -> IntoTagged
123
+
124
+ export type FnIntoString = (
125
+ innerDef: Def
126
+ ) -> Def
127
+
128
+ export type FnIntoNumber = (
129
+ innerDef: Def,
130
+ base: number?
131
+ ) -> Def
132
+
133
+ export type FnIntoDefault = (
134
+ innerDef: Def,
135
+ defaultValue: any
136
+ ) -> Def
137
+
138
+ export type FnRetype = (
139
+ def: Def
140
+ ) -> Def
141
+
142
+ export type FnUntyped = (
143
+ def: Def
144
+ ) -> Def
145
+
146
+ export type FnNicknamed = (
147
+ innerDef: Def,
148
+ newName: string
149
+ ) -> Def
150
+
151
+ export type FnCastOrError = (
152
+ def: Def,
153
+ x: unknown
154
+ ) -> unknown
155
+
156
+ return nil
package/src/index.d.ts ADDED
@@ -0,0 +1,71 @@
1
+ type Some<Value> = { some: true; value: Value };
2
+ type None = { some: false; reason?: string };
3
+ type Maybe<Value> = Some<Value> | None;
4
+
5
+ declare namespace ty {
6
+ export type Static<T> = T extends Def<infer CastTo> ? CastTo : never;
7
+
8
+ export interface Def<CastTo> {
9
+ ExpectsType: string;
10
+ NotOfTypeError: string;
11
+ Matches(value: unknown): boolean;
12
+ Cast(value: unknown): Maybe<CastTo>;
13
+
14
+ Optional(): Def<CastTo | undefined>;
15
+ IgnoreInvalid(): Def<CastTo | undefined>;
16
+ Or<L>(last: Def<L>): Def<CastTo | L>;
17
+ And<L>(last: Def<L>): Def<CastTo & L>;
18
+ IntoTagged<Tag>(tag: Tag): Def<{ __tag: Tag; value: CastTo }>;
19
+ IntoString(): Def<string>;
20
+ IntoNumber(base?: number): Def<number>;
21
+ IntoDefault(defaultValue: NonNullable<CastTo>): Def<NonNullable<CastTo>>;
22
+ Retype<T>(this: Def<any>): Def<T>;
23
+ Untyped(): Def<any>;
24
+ Nicknamed(newName: string): Def<CastTo>;
25
+ CastOrError(x: unknown): CastTo;
26
+ }
27
+
28
+ export function Predicate<T>(predicate: (x: unknown) => x is T): Def<T>;
29
+ export function Typeof<T extends keyof CheckableTypes>(typeString: T): Def<CheckableTypes[T]>;
30
+ export function Just<T>(literal: T, type?: string): Def<T>;
31
+ export function Optional<T>(innerDef: Def<T>): Def<T | undefined>;
32
+ export function IgnoreInvalid<T>(innerDef: T): Def<T | undefined>;
33
+ export function Or<F, L>(first: Def<F>, last: Def<L>): Def<F | L>;
34
+ export function And<F, L>(first: Def<F>, last: Def<L>): Def<F & L>;
35
+ export function MapOf<K extends string | number | symbol, V>(keys: Def<K>, values: Def<V>): Def<Record<K, V>>;
36
+ export function MapOf<K, V>(keys: Def<K>, values: Def<V>): Def<Map<K, V>>;
37
+ export function Array<V>(values: Def<V>): Def<V[]>;
38
+ export function Struct<T extends Record<string, Def<unknown>>>(
39
+ options: { exhaustive: boolean },
40
+ object: T,
41
+ ): Def<{ [K in keyof T]: Static<T[K]> }>;
42
+ export function Tuple<T extends Array<Def<unknown>>>(
43
+ options: { exhaustive: boolean },
44
+ tuple: T,
45
+ ): Def<{ [K in keyof T]: Static<T[K]> }>;
46
+ export function IntoTagged<Tag, T>(innerDef: Def<T>, tag: Tag): Def<{ __tag: Tag; value: T }>;
47
+ export function IntoString<T>(innerDef: Def<T>): Def<string>;
48
+ export function IntoNumber<T>(innerDef: Def<T>, base?: number): Def<number>;
49
+ export function IntoDefault<T>(innerDef: Def<T>, defaultValue: NonNullable<T>): Def<NonNullable<T>>;
50
+ export function Retype<T>(def: Def<any>): Def<T>;
51
+ export function Untyped<T>(def: Def<T>): Def<any>;
52
+ export function Nicknamed<T>(innerDef: Def<T>, newName: string): Def<T>;
53
+ export function CastOrError<T>(def: Def<T>, x: unknown): T;
54
+
55
+ export const Unknown: Def<unknown>;
56
+ export const Never: Def<never>;
57
+
58
+ export const Number: Def<number>;
59
+ export const Boolean: Def<boolean>;
60
+ export const String: Def<string>;
61
+ export const Thread: Def<thread>;
62
+ export const Table: Def<object>;
63
+ export const Function: Def<(...args: unknown[]) => unknown>;
64
+
65
+ export const True: Def<true>;
66
+ export const False: Def<false>;
67
+ export const Nil: Def<undefined>;
68
+ }
69
+
70
+ export = ty;
71
+ export as namespace ty;
package/src/init.luau ADDED
@@ -0,0 +1,73 @@
1
+ --!strict
2
+ --!nolint LocalShadow
3
+ -- ty (Elttob Suite monorepo edition - no static types)
4
+ -- (c) Daniel P H Fox, licensed under MIT
5
+
6
+ local Package = script.Parent
7
+ local Maybe = require(Package.Maybe)
8
+
9
+ local Types = require(Package.ty.Types)
10
+ local Def = require(Package.ty.Def)
11
+
12
+ export type Def = Types.Def
13
+ export type Constants = Types.Constants
14
+ export type Methods = Types.Methods
15
+ export type Optional = Types.Optional
16
+ export type Or = Types.Or
17
+ export type And = Types.And
18
+ export type MapOf = Types.MapOf
19
+ export type Array = Types.Array
20
+ export type Struct = Types.Struct
21
+ export type IntoTagged = Types.IntoTagged
22
+
23
+ -- TODO: need methods to limit table size and depth
24
+ -- TODO: need methods to handle cyclic data
25
+ local methods: Types.Methods = {
26
+ Predicate = require(Package.ty.Functions.Predicate),
27
+ Typeof = require(Package.ty.Functions.Typeof),
28
+ Just = require(Package.ty.Functions.Just),
29
+ Optional = require(Package.ty.Functions.Optional),
30
+ IgnoreInvalid = require(Package.ty.Functions.IgnoreInvalid),
31
+ Or = require(Package.ty.Functions.Or),
32
+ And = require(Package.ty.Functions.And),
33
+ MapOf = require(Package.ty.Functions.MapOf),
34
+ Array = require(Package.ty.Functions.Array),
35
+ Struct = require(Package.ty.Functions.Struct),
36
+ Tuple = require(Package.ty.Functions.Tuple),
37
+ IntoTagged = require(Package.ty.Functions.IntoTagged),
38
+ IntoString = require(Package.ty.Functions.IntoString),
39
+ IntoNumber = require(Package.ty.Functions.IntoNumber),
40
+ IntoDefault = require(Package.ty.Functions.IntoDefault),
41
+ Retype = require(Package.ty.Functions.Retype),
42
+ Untyped = require(Package.ty.Functions.Untyped),
43
+ Nicknamed = require(Package.ty.Functions.Nicknamed),
44
+ CastOrError = require(Package.ty.Functions.CastOrError)
45
+ }
46
+ Def.methodMeta.__index = methods
47
+
48
+ local constants: Types.Constants = {
49
+ Unknown = Def.new(
50
+ "unknown",
51
+ function(self, x) return true end,
52
+ function(self, x) return Maybe.Some(x) end
53
+ ),
54
+ Never = Def.new(
55
+ "never",
56
+ function(self, x) return false end,
57
+ function(self, x) return Maybe.None(self.NotOfTypeError) end
58
+ ),
59
+
60
+ Number = methods.Typeof("number"),
61
+ Boolean = methods.Typeof("boolean"),
62
+ String = methods.Typeof("string"),
63
+ Thread = methods.Typeof("thread"),
64
+ Table = methods.Typeof("table"),
65
+ Function = methods.Typeof("function"),
66
+
67
+ True = methods.Just(true) :: any,
68
+ False = methods.Just(false) :: any,
69
+ Nil = methods.Just(nil)
70
+ }
71
+
72
+ local ty: Types.Methods & Types.Constants = setmetatable(constants, Def.methodMeta) :: any
73
+ return ty