Resposta rápida
- Tipos
- oito, cada um com uma letra —
C,N,L,D,O,U,BeA. - Como descobrir
ValType(xValor)devolve a letra do valor que está na variável agora.- Data no banco
- vai como texto
AAAAMMDD;DToS()eSToD()fazem a ida e a volta. - Tipagem com As
- documenta e deixa o compilador recusar atribuição claramente errada; não transforma a variável em cofre.
Nesta página8 seções
No AdvPL o tipo pertence ao valor, não à variável. A mesma xRet pode
começar numérica, virar texto três linhas abaixo e terminar Nil. Isso é
flexível, e é por isso que type mismatch está entre os erros de execução
mais frequentes em customização. Este artigo mostra os tipos, como testar e como converter.
Onde a variável vive — Local, Private, Static — é outro assunto, em
Escopo de variáveis em AdvPL.
A tabela
| Tipo | Letra | Literal de exemplo | Prefixo usual |
|---|---|---|---|
| Caractere | C | "PGV-001" | c |
| Numérico | N | 1250.75 | n |
| Lógico | L | .T. / .F. | l |
| Data | D | SToD("20260917") | d |
| Objeto | O | FWTemporaryTable():New("TRB") | o |
| Indefinido | U | Nil | x ou u |
| Bloco de código | B | `{ | x |
| Array | A | { "A", 1, .T. } | a |
Data não tem literal: ela nasce de função (Date(), SToD(), CToD()) ou de
campo de tabela. O prefixo é convenção, não regra da linguagem — mas é ela que
permite ler nTotal + cDesc e ver o erro antes de compilar.
Array tem artigo próprio: Arrays em AdvPL.
ValType e o teste antes do uso
ValType() recebe o valor e devolve a letra. É o teste que protege código que
recebe dado de fora — parâmetro opcional, retorno de função de terceiro,
posição de PARAMIXB:
User Function PGV003(xQtd)
Local nQtd := 0
If ValType(xQtd) == "N"
nQtd := xQtd
ElseIf ValType(xQtd) == "C"
nQtd := Val(xQtd)
EndIf
// U (parâmetro não informado) cai aqui com nQtd zerado
Return nQtdNão confunda com Type(), que recebe o nome entre aspas e avalia a
expressão — é a que se usa para saber se uma Private de outra rotina existe:
Type("aCols") == "A". ValType(aCols) com aCols inexistente estoura erro
antes de o teste acontecer. O caso real está em
PARAMIXB, o array que às vezes não é seu.
Conversões que você vai escrever toda semana
Local nValor := 1250.75
Local dHoje := Date()
Local cTexto := ""
cTexto := cValToChar(nValor) // "1250.75"
cTexto := Str(nValor, 10, 2) // " 1250.75" — com espaços à esquerda
cTexto := AllTrim(Str(nValor, 10, 2)) // "1250.75"
cTexto := StrZero(42, 6) // "000042"
cTexto := Transform(nValor, "@E 999,999.99") // " 1.250,75"
nValor := Val("1250.75") // 1250.75
nValor := Val("1250,75") // 1250 — a vírgula encerra a leitura
cTexto := DToS(dHoje) // "20260917"
dHoje := SToD("20260917")
cTexto := DToC(dHoje) // "17/09/2026" ou "17/09/26"
dHoje := CToD("17/09/2026")Quatro observações que valem o bloco inteiro:
cValToCharaceita qualquer tipo simples — número, data, lógico — e é a escolha certa para log. Concatenar número direto em string é otype mismatchmais comum que existe.Stralinha à direita. SemAllTrim, a chave de busca sai com espaços e oDbSeeknão acha nada.Valnão entende vírgula decimal. Valor digitado ou vindo de planilha em formato brasileiro precisa deStrTran(cValor, ",", ".")antes, e de tirar o ponto de milhar antes disso.DToCeCToDdependem do formato de data da sessão (ano com dois ou quatro dígitos, ordem dia/mês). Servem para mostrar ao usuário. Para gravar, comparar ou montar SQL, useDToS/SToD, que não mudam nunca.
Data no banco é texto
O dicionário diz D; no banco ela vira texto de 8 posições, AAAAMMDD. É
por isso que em SQL a data se compara como string, e é por isso que DToS
existe:
SELECT X3_CAMPO, X3_TIPO, X3_TAMANHO
FROM SX3010
WHERE X3_CAMPO IN ('C5_EMISSAO', 'C5_NUM')
AND D_E_L_E_T_ = ' 'Na workarea, SC5->C5_EMISSAO já chega convertido para tipo D. Numa query
embarcada, chega como texto e precisa de SToD — ou de TCSetField — antes de
qualquer conta. O detalhe fica em
Consultas SQL em AdvPL.
Aritmética de datas
Data mais número dá data; data menos data dá número de dias:
Local dVenc := dDataBase + 30 // trinta dias corridos
Local nDias := dVenc - dDataBase // 30
Local dMes := MonthSum(SToD("20260131"), 1) // 28/02/2026, não 31/02Somar dias com + é uso corrente no próprio padrão — em FN694Consol(),
uma linha soma dDatabase + 1 assim, sem cerimônia. O que não funciona
com + é mês e ano: + 30 não é “um mês”, porque nem todo mês tem o mesmo
número de dias. Para isso existe MonthSum(dDate, nMonth), ao lado de
DaySum(dDate, nDays).
E lembre que dDataBase é a data informada no login, não a do servidor —
Date() é a do AppServer. Rotina agendada que usa uma no lugar da outra
processa o dia errado.
Bloco de código e Eval
Bloco de código é uma função sem nome guardada numa variável. Ele só executa
quando alguém chama Eval(), que devolve o valor da última expressão:
Local bDesc := {|nBase, nPerc| nBase - (nBase * nPerc / 100) }
Local nFinal := Eval(bDesc, 200, 10) // 180
Local bLog := {|| ConOut("PGV003 " + cValToChar(nFinal)), .T. }
Eval(bLog) // loga e devolve.T.É assim que o framework recebe regra sua: validação de campo, ação de botão,
filtro de browse. Quando uma rotina padrão pede “um bloco”, ela vai chamar
Eval nele no momento dela, não no seu. Um bloco pode ler as variáveis Local
de onde foi criado, e isso é o que o torna útil — e o que torna difícil
depurar quando ele roda longe dali.
Objeto e Nil
Tipo O é o que sai de um construtor (:New()). Tipo U é a ausência de
valor: variável declarada sem atribuição, parâmetro não passado, função que
retorna Nil. Todo objeto que você cria merece FreeObj(oObj) no fim, e todo
parâmetro opcional merece um teste de U logo na entrada da função — a
ordem dessas declarações está em
Estrutura de um fonte AdvPL.
Tipagem declarada com As
A declaração pode dizer o tipo:
Local cCliente As Character
Local nTotal As Numeric
Local lOk As Logical
Local dEmissao As Date
Local oModelo As Object
Local bRegra As Codeblock
Local aItens As Array
Local xRet As VariantNão é novidade de TLPP: o próprio AdvPL já usa As Character amplamente,
em funções como AF012Inv(), e As Variant para o que aceita qualquer
tipo, como em FINFK5BUSCA() — um sinal de que o parâmetro recebe mais de
um tipo dependendo de quem chama, e que quem lê a função precisa testar
antes de usar.
O que ela garante: o leitor sabe o que esperar, e o compilador recusa uma
atribuição cujo tipo ele consegue ver e que não bate — um literal numérico numa
variável Character, por exemplo.
O que ela não garante: o compilador não conhece o tipo do retorno de toda
função, de campo de tabela, de Private alheia nem de posição de array. Valor
que chega por esses caminhos passa. Continue testando com ValType tudo que
vem de fora; As complementa o teste, não o substitui.
Quando o fonte com tipagem não compila fora do TDS, as causas usuais estão em Oito armadilhas ao compilar AdvPL e TLPP fora do TDS. O próximo passo da série é o que se faz com esses valores: Operadores em AdvPL.
Se um type mismatch está custando tempo e você quer uma segunda leitura,
fale com a gente.
Referências
TDNtdn.totvs.com
TDN — ValTypeTDNtdn.totvs.com
TDN — cValToCharTDNtdn.totvs.com
TDN — DToSTDNtdn.totvs.com
TDN — CToDTDNtdn.totvs.com
TDN — TransformTDNtdn.totvs.com
TDN — Eval
Continue por aqui
- Vídeo
DesenvolvimentoTutorial
Estrutura de um fonte AdvPL: do include ao Return
Estrutura de um fonte AdvPL: o que tem num .prw ou .tlpp, como vira APO no RPO e a diferença real entre User Function e Static Function.