Sistema de Tipos
Sistema de Tipos
Seção intitulada “Sistema de Tipos”Experimental. Algumas limitações são esperadas.
O Wippy inclui um sistema de tipos gradual com verificação sensível ao fluxo. Tipos são não-anuláveis por padrão.
Primitivos
Seção intitulada “Primitivos”local n: number = 3.14local i: integer = 42 -- integer é subtipo de numberlocal s: string = "hello"local b: boolean = truelocal a: any = "anything" -- dinâmico explícito (opt-out da verificação)local u: unknown = something -- deve ser estreitado antes do usoany vs unknown
Seção intitulada “any vs unknown”-- any: opt-out da verificação de tiposlocal a: any = get_data()a.foo.bar.baz() -- sem erro, pode falhar em tempo de execução
-- unknown: desconhecido seguro, deve ser estreitado antes do usolocal u: unknown = get_data()u.foo -- ERRO: não é possível acessar propriedade de unknownif type(u) == "table" then -- u estreitado para table aquiendSegurança contra Nil
Seção intitulada “Segurança contra Nil”Tipos são não-anuláveis por padrão. Use ? para valores opcionais:
local x: number = nil -- ERRO: nil não atribuível a numberlocal y: number? = nil -- OK: number? significa "number ou nil"local z: number? = 42 -- OKEstreitamento por Fluxo de Controle
Seção intitulada “Estreitamento por Fluxo de Controle”O verificador de tipos rastreia o fluxo de controle:
local function process(x: number?): number if x ~= nil then return x -- x é number aqui end return 0end
-- Padrão de retorno antecipadolocal user, err = get_user(123)if err then return nil, err end-- user estreitado para não-nil aqui
-- Ou padrãolocal val = get_value() or 0 -- val: numberTipos União
Seção intitulada “Tipos União”local val: number | string = get_value()
if type(val) == "number" then print(val + 1) -- val: numberelse print(val:upper()) -- val: stringendTipos Literais
Seção intitulada “Tipos Literais”type Status = "pending" | "active" | "done"
local s: Status = "pending" -- OKlocal s: Status = "invalid" -- ERROTipos de Função
Seção intitulada “Tipos de Função”local function add(a: number, b: number): number return a + bend
-- Múltiplos retornoslocal function div_mod(a: number, b: number): (number, number) return math.floor(a / b), a % bend
-- Retornos de erro (idioma Lua)local function fetch(url: string): (string?, error?) -- retorna (data, nil) ou (nil, error)end
-- Tipos de função de primeira classelocal double: (number) -> number = function(x: number): number return x * 2endFunções Variádicas
Seção intitulada “Funções Variádicas”local function sum(...: number): number local total: number = 0 for _, v in ipairs({...}) do total = total + v end return totalendTipos Record
Seção intitulada “Tipos Record”type User = {name: string, age: number}
local u: User = {name = "alice", age = 25}Campos Opcionais
Seção intitulada “Campos Opcionais”type Config = { host: string, port: number, timeout?: number, debug?: boolean}
local cfg: Config = {host = "localhost", port = 8080} -- OKGenéricos
Seção intitulada “Genéricos”local function identity<T>(x: T): T return xend
local n: number = identity(42)local s: string = identity("hello")Genéricos Restritos
Seção intitulada “Genéricos Restritos”type HasName = {name: string}
local function greet<T: HasName>(obj: T): string return "Hello, " .. obj.nameend
greet({name = "Alice"}) -- OKgreet({age = 30}) -- ERRO: 'name' ausenteTipos Interseção
Seção intitulada “Tipos Interseção”Combine múltiplos tipos:
type Named = {name: string}type Aged = {age: number}type Person = Named & Aged
local p: Person = {name = "Alice", age = 30}Uniões Discriminadas
Seção intitulada “Uniões Discriminadas”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 endendO Tipo never
Seção intitulada “O Tipo never”never é o tipo bottom — nenhum valor existe:
function fail(msg: string): never error(msg)endPadrão de Tratamento de Erros
Seção intitulada “Padrão de Tratamento de Erros”O verificador entende o idioma de erro do Lua:
local value, err = call()if err then -- value é nil aqui return nil, errend-- value é não-nil aqui, err é nilprint(value)Asserção de Não-Nil
Seção intitulada “Asserção de Não-Nil”Use ! para afirmar que uma expressão é não-nil:
local user: User? = get_user()local name = user!.name -- afirma que user é não-nilSe o valor for nil em tempo de execução, um erro é levantado. Use quando souber que um valor não pode ser nil mas o verificador de tipos não consegue prová-lo.
Conversões de Tipo
Seção intitulada “Conversões de Tipo”Conversão Segura (Validação)
Seção intitulada “Conversão Segura (Validação)”Chame um tipo como uma função para validar e converter:
local data: any = get_json()local user = User(data) -- valida e retorna Userlocal name = user.name -- acesso seguro a campoFunciona com primitivos e tipos personalizados:
local x: any = get_value()local s = string(x) -- converte para stringlocal n = integer(x) -- converte para integerlocal b = boolean(x) -- converte para boolean
type Point = {x: number, y: number}local p = Point(data) -- valida estrutura do recordMétodo Type:is()
Seção intitulada “Método Type:is()”Valida sem lançar exceção, retorna (value, nil) ou (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 é Point válidoelse return nil, err -- validação falhouendO resultado é estreitado em condicionais:
if Point:is(data) then local p: Point = data -- data estreitado para PointendConversão Insegura
Seção intitulada “Conversão Insegura”Use :: ou as para conversões não verificadas:
local data: any = get_data()local user = data :: User -- sem verificação em tempo de execuçãolocal user = data as User -- igual a ::Use com moderação. Conversões inseguras ignoram a validação e podem causar erros em tempo de execução se o valor não corresponder ao tipo.
Reflexão de Tipos
Seção intitulada “Reflexão de Tipos”Tipos são valores de primeira classe com métodos de introspecção.
Kind e Name
Seção intitulada “Kind e Name”print(Number:kind()) -- "number"print(Point:kind()) -- "record"print(Point:name()) -- "Point"Campos de Record
Seção intitulada “Campos de Record”Itera sobre campos do record:
type User = {name: string, age: number}
for name, typ in User:fields() do print(name, typ:kind())end-- name string-- age numberAcessa tipos de campos individuais:
local nameType = User.name -- tipo do campo 'name'print(nameType:kind()) -- "string"Tipos de Coleção
Seção intitulada “Tipos de Coleção”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 Opcionais
Seção intitulada “Tipos Opcionais”local opt: number? = nillocal optType = typeof(opt)print(optType:kind()) -- "optional"print(optType:inner():kind()) -- "number"Tipos União
Seção intitulada “Tipos União”type Status = "pending" | "active" | "done"
for variant in Status:variants() do print(variant)endTipos de Função
Seção intitulada “Tipos de Função”local fn: (number, string) -> boolean
local fnType = typeof(fn)for param in fnType:params() do print(param:kind())endprint(fnType:ret():kind()) -- "boolean"Comparação de Tipos
Seção intitulada “Comparação de Tipos”print(Number == Number) -- trueprint(Integer <= Number) -- true (subtipo)print(Integer < Number) -- true (subtipo estrito)Tipos como Chaves de Tabela
Seção intitulada “Tipos como Chaves de Tabela”local handlers = {}handlers[Number] = function() return "number handler" endhandlers[String] = function() return "string handler" end
local h = handlers[typeof(value)]if h then h() endAnotações de Tipo
Seção intitulada “Anotações de Tipo”Adicione tipos a assinaturas de função:
-- Tipos de parâmetro e retornolocal function process(input: string): number return #inputend
-- Tipos de variáveis locaislocal count: number = 0
-- Aliases de tipotype StringArray = {string}type StringMap = {[string]: number}Validadores de Tipo
Seção intitulada “Validadores de Tipo”Adicione restrições de validação em tempo de execução aos tipos usando anotações:
-- Validador únicolocal x: number @min(0) = 1
-- Múltiplos validadoreslocal x: number @min(0) @max(100) = 50
-- Padrão de stringlocal email: string @pattern("^.+@.+$") = "test@example.com"
-- Validador sem argumentoslocal x: number @integer = 42Validadores Embutidos
Seção intitulada “Validadores Embutidos”| Validador | Aplica-se a | Exemplo |
|---|---|---|
@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 Campos de Record
Seção intitulada “Validadores de Campos de Record”type User = { age: number @min(0) @max(150), name: string @min_len(1) @max_len(100)}Validadores de Elementos de Array
Seção intitulada “Validadores de Elementos de Array”local scores: {number @min(0) @max(100)} = {85, 90}Validadores de Membros de União
Seção intitulada “Validadores de Membros de União”local id: number @min(1) | string @min_len(1) = 1Regras de Variância
Seção intitulada “Regras de Variância”| Posição | Variância | Descrição |
|---|---|---|
| Campo somente leitura | Covariante | Pode usar subtipo |
| Campo mutável | Invariante | Deve corresponder exatamente |
| Parâmetro de função | Contravariante | Pode usar supertipo |
| Retorno de função | Covariante | Pode usar subtipo |
Subtipagem
Seção intitulada “Subtipagem”integeré um subtipo denumberneveré um subtipo de todos os tipos- Todos os tipos são subtipos de
any - Subtipagem de união:
Aé subtipo deA | B
Adoção Gradual
Seção intitulada “Adoção Gradual”Adicione tipos incrementalmente — código sem tipos continua funcionando:
-- Código existente funciona inalteradofunction old_function(x) return x + 1end
-- Novo código recebe tiposfunction new_function(x: number): number return x + 1endComece adicionando tipos a:
- Assinaturas de função em fronteiras de API
- Handlers HTTP e consumidores de fila
- Lógica de negócio crítica
Verificação de Tipos
Seção intitulada “Verificação de Tipos”Execute o verificador de tipos:
wippy lintReporta erros de tipo sem executar o código.