Sistema de Tipos
Sistema de Tipos
Sección titulada «Sistema de Tipos»Experimental. Se esperan algunas limitaciones.
Wippy incluye un sistema de tipos gradual con verificación sensible al flujo. Los tipos no son anulables por defecto.
Primitivos
Sección titulada «Primitivos»local n: number = 3.14local i: integer = 42 -- integer is subtype of numberlocal s: string = "hello"local b: boolean = truelocal a: any = "anything" -- explicit dynamic (opt-out of checking)local u: unknown = something -- must narrow before useany vs unknown
Sección titulada «any vs unknown»-- any: opt-out of type checkinglocal a: any = get_data()a.foo.bar.baz() -- no error, may crash at runtime
-- unknown: safe unknown, must narrow before uselocal u: unknown = get_data()u.foo -- ERROR: cannot access property of unknownif type(u) == "table" then -- u narrowed to table hereendSeguridad de Nil
Sección titulada «Seguridad de Nil»Los tipos no son anulables por defecto. Use ? para valores opcionales:
local x: number = nil -- ERROR: nil not assignable to numberlocal y: number? = nil -- OK: number? means "number or nil"local z: number? = 42 -- OKEstrechamiento por Flujo de Control
Sección titulada «Estrechamiento por Flujo de Control»El verificador de tipos rastrea el flujo de control:
local function process(x: number?): number if x ~= nil then return x -- x is number here end return 0end
-- Early return patternlocal user, err = get_user(123)if err then return nil, err end-- user narrowed to non-nil here
-- Or defaultlocal val = get_value() or 0 -- val: numberTipos Unión
Sección titulada «Tipos Unión»local val: number | string = get_value()
if type(val) == "number" then print(val + 1) -- val: numberelse print(val:upper()) -- val: stringendTipos Literales
Sección titulada «Tipos Literales»type Status = "pending" | "active" | "done"
local s: Status = "pending" -- OKlocal s: Status = "invalid" -- ERRORTipos de Función
Sección titulada «Tipos de Función»local function add(a: number, b: number): number return a + bend
-- Multiple returnslocal function div_mod(a: number, b: number): (number, number) return math.floor(a / b), a % bend
-- Error returns (Lua idiom)local function fetch(url: string): (string?, error?) -- returns (data, nil) or (nil, error)end
-- First-class function typeslocal double: (number) -> number = function(x: number): number return x * 2endFunciones Variádicas
Sección titulada «Funciones Variádicas»local function sum(...: number): number local total: number = 0 for _, v in ipairs({...}) do total = total + v end return totalendTipos Registro
Sección titulada «Tipos Registro»type User = {name: string, age: number}
local u: User = {name = "alice", age = 25}Campos Opcionales
Sección titulada «Campos Opcionales»type Config = { host: string, port: number, timeout?: number, debug?: boolean}
local cfg: Config = {host = "localhost", port = 8080} -- OKGenéricos
Sección titulada «Genéricos»local function identity<T>(x: T): T return xend
local n: number = identity(42)local s: string = identity("hello")Genéricos Restringidos
Sección titulada «Genéricos Restringidos»type HasName = {name: string}
local function greet<T: HasName>(obj: T): string return "Hello, " .. obj.nameend
greet({name = "Alice"}) -- OKgreet({age = 30}) -- ERROR: missing 'name'Tipos Intersección
Sección titulada «Tipos Intersección»Combinan múltiples tipos:
type Named = {name: string}type Aged = {age: number}type Person = Named & Aged
local p: Person = {name = "Alice", age = 30}Uniones Etiquetadas
Sección titulada «Uniones Etiquetadas»type Result<T, E> = | {ok: true, value: T} | {ok: false, error: E}
type LoadState = | {status: "loading"} | {status: "loaded", data: User} | {status: "error", message: string}
local function render(state: LoadState): string if state.status == "loading" then return "Loading..." elseif state.status == "loaded" then return "Hello, " .. state.data.name elseif state.status == "error" then return "Error: " .. state.message endendEl Tipo never
Sección titulada «El Tipo never»never es el tipo de fondo — no existen valores:
function fail(msg: string): never error(msg)endPatrón de Manejo de Errores
Sección titulada «Patrón de Manejo de Errores»El verificador entiende el modismo de error de Lua:
local value, err = call()if err then -- value is nil here return nil, errend-- value is non-nil here, err is nilprint(value)Aserción de No-Nil
Sección titulada «Aserción de No-Nil»Use ! para afirmar que una expresión no es nil:
local user: User? = get_user()local name = user!.name -- assert user is non-nilSi el valor es nil en tiempo de ejecución, se lanza un error. Úselo cuando sepa que un valor no puede ser nil pero el verificador de tipos no puede demostrarlo.
Casts de Tipo
Sección titulada «Casts de Tipo»Cast Seguro (Validación)
Sección titulada «Cast Seguro (Validación)»Llame a un tipo como una función para validar y hacer cast:
local data: any = get_json()local user = User(data) -- validates and returns Userlocal name = user.name -- safe field accessFunciona con primitivos y tipos personalizados:
local x: any = get_value()local s = string(x) -- cast to stringlocal n = integer(x) -- cast to integerlocal b = boolean(x) -- cast to boolean
type Point = {x: number, y: number}local p = Point(data) -- validates record structureMétodo Type:is()
Sección titulada «Método Type:is()»Valida sin lanzar excepción, retorna (value, nil) o (nil, error):
type Point = {x: number, y: number}local data: any = get_input()
local p, err = Point:is(data)if p then local sum = p.x + p.y -- p is valid Pointelse return nil, err -- validation failedendEl resultado se estrecha en condicionales:
if Point:is(data) then local p: Point = data -- data narrowed to PointendCast Inseguro
Sección titulada «Cast Inseguro»Use :: o as para casts no verificados:
local data: any = get_data()local user = data :: User -- no runtime checklocal user = data as User -- same as ::Úselo con moderación. Los casts inseguros omiten la validación y pueden causar errores en tiempo de ejecución si el valor no coincide con el tipo.
Reflexión de Tipos
Sección titulada «Reflexión de Tipos»Los tipos son valores de primera clase con métodos de introspección.
Kind y Nombre
Sección titulada «Kind y Nombre»print(Number:kind()) -- "number"print(Point:kind()) -- "record"print(Point:name()) -- "Point"Campos de Registro
Sección titulada «Campos de Registro»Itere sobre los campos de un registro:
type User = {name: string, age: number}
for name, typ in User:fields() do print(name, typ:kind())end-- name string-- age numberAcceda a tipos de campos individuales:
local nameType = User.name -- type of 'name' fieldprint(nameType:kind()) -- "string"Tipos de Colección
Sección titulada «Tipos de Colección»local arr: {number} = {1, 2, 3}local arrType = typeof(arr)print(arrType:elem():kind()) -- "number"
local map: {[string]: number} = {}local mapType = typeof(map)print(mapType:key():kind()) -- "string"print(mapType:val():kind()) -- "number"Tipos Opcionales
Sección titulada «Tipos Opcionales»local opt: number? = nillocal optType = typeof(opt)print(optType:kind()) -- "optional"print(optType:inner():kind()) -- "number"Tipos Unión
Sección titulada «Tipos Unión»type Status = "pending" | "active" | "done"
for variant in Status:variants() do print(variant)endTipos de Función
Sección titulada «Tipos de Función»local fn: (number, string) -> boolean
local fnType = typeof(fn)for param in fnType:params() do print(param:kind())endprint(fnType:ret():kind()) -- "boolean"Comparación de Tipos
Sección titulada «Comparación de Tipos»print(Number == Number) -- trueprint(Integer <= Number) -- true (subtype)print(Integer < Number) -- true (strict subtype)Tipos como Claves de Tabla
Sección titulada «Tipos como Claves de Tabla»local handlers = {}handlers[Number] = function() return "number handler" endhandlers[String] = function() return "string handler" end
local h = handlers[typeof(value)]if h then h() endAnotaciones de Tipo
Sección titulada «Anotaciones de Tipo»Agregue tipos a las firmas de función:
-- Parameter and return typeslocal function process(input: string): number return #inputend
-- Local variable typeslocal count: number = 0
-- Type aliasestype StringArray = {string}type StringMap = {[string]: number}Validadores de Tipo
Sección titulada «Validadores de Tipo»Agregue restricciones de validación en tiempo de ejecución a los tipos usando anotaciones:
-- Single validatorlocal x: number @min(0) = 1
-- Multiple validatorslocal x: number @min(0) @max(100) = 50
-- String patternlocal email: string @pattern("^.+@.+$") = "test@example.com"
-- No-arg validatorlocal x: number @integer = 42Validadores Integrados
Sección titulada «Validadores Integrados»| Validador | Aplica a | Ejemplo |
|---|---|---|
@min(n) | number | local x: number @min(0) = 1 |
@max(n) | number | local x: number @max(100) = 50 |
@min_len(n) | string, array | local s: string @min_len(1) = "hi" |
@max_len(n) | string, array | local s: string @max_len(10) = "hi" |
@pattern(regex) | string | local email: string @pattern("^.+@.+$") = "a@b.com" |
Validadores de Campo de Registro
Sección titulada «Validadores de Campo de Registro»type User = { age: number @min(0) @max(150), name: string @min_len(1) @max_len(100)}Validadores de Elemento de Array
Sección titulada «Validadores de Elemento de Array»local scores: {number @min(0) @max(100)} = {85, 90}Validadores de Miembro de Unión
Sección titulada «Validadores de Miembro de Unión»local id: number @min(1) | string @min_len(1) = 1Reglas de Varianza
Sección titulada «Reglas de Varianza»| Posición | Varianza | Descripción |
|---|---|---|
| Campo de solo lectura | Covariante | Puede usar subtipo |
| Campo mutable | Invariante | Debe coincidir exactamente |
| Parámetro de función | Contravariante | Puede usar supertipo |
| Retorno de función | Covariante | Puede usar subtipo |
Subtipado
Sección titulada «Subtipado»integeres un subtipo denumberneveres un subtipo de todos los tipos- Todos los tipos son subtipos de
any - Subtipado de unión:
Aes subtipo deA | B
Adopción Gradual
Sección titulada «Adopción Gradual»Agregue tipos incrementalmente — el código sin tipos sigue funcionando:
-- Existing code works unchangedfunction old_function(x) return x + 1end
-- New code gets typesfunction new_function(x: number): number return x + 1endComience agregando tipos a:
- Firmas de funciones en los límites de la API
- Handlers HTTP y consumidores de cola
- Lógica de negocio crítica
Verificación de Tipos
Sección titulada «Verificación de Tipos»Ejecute el verificador de tipos:
wippy lintReporta errores de tipo sin ejecutar código.