Existe uma maneira de analisar strings como JSON em Typescript.
Exemplo: Em JS, podemos usar JSON.parse(). Existe uma função semelhante em Typescript?
Eu tenho uma string de objeto JSON da seguinte maneira:
{"name": "Bob", "error": false}
Existe uma maneira de analisar strings como JSON em Typescript.
Exemplo: Em JS, podemos usar JSON.parse(). Existe uma função semelhante em Typescript?
Eu tenho uma string de objeto JSON da seguinte maneira:
{"name": "Bob", "error": false}
JSON.parseobtém um objeto como resultado e não um string(veja minha resposta para mais informações). Se você quiser transformar um objeto em uma string, você precisa usar JSON.stringify.
Respostas:
Texto tipográfico é (um superconjunto de) javascript, então você apenas usa JSON.parsecomo faria em javascript:
let obj = JSON.parse(jsonString);
Apenas que no texto digitado você pode ter um tipo para o objeto resultante:
interface MyObj {
myString: string;
myNumber: number;
}
let obj: MyObj = JSON.parse('{ "myString": "string", "myNumber": 4 }');
console.log(obj.myString);
console.log(obj.myNumber);
'{ "myString": "string", "myNumber": 4 }'por '{ "myString": "string", "myNumberBAD": 4 }'não falhará e obj.myNumber retornará indefinido.
Json.parse(text).validate[MyObj]. playframework.com/documentation/2.6.x/ScalaJson como você pode fazer o mesmo no texto datilografado (talvez haja uma biblioteca externa para fazer isso?)?
MyObjnão existe. Existem muitos outros tópicos no SO sobre este assunto, por exemplo: Verifique se um objeto implementa uma interface em tempo de execução com TypeScript
JSON.parseVocê pode continuar a usar JSON.parse , já que TS é um superconjunto JS. Resta um problema: JSON.parsedevoluções any, o que prejudica a segurança de tipo. Aqui estão duas opções para tipos mais fortes:
Protetores de tipo personalizado são a solução mais simples e muitas vezes suficientes para validação de dados externos:
// For example, you expect to parse a given value with `MyType` shape
type MyType = { name: string; description: string; }
// Validate this value with a custom type guard
function isMyType(o: any): o is MyType {
return "name" in o && "description" in o
}
UMA JSON.parse wrapper pode então pegar um protetor de tipo como entrada e retornar o valor digitado analisado:
const safeJsonParse = <T>(guard: (o: any) => o is T) => (text: string): ParseResult<T> => {
const parsed = JSON.parse(text)
return guard(parsed) ? { parsed, hasError: false } : { hasError: true }
}
type ParseResult<T> =
| { parsed: T; hasError: false; error?: undefined }
| { parsed?: undefined; hasError: true; error?: unknown }
Exemplo de uso:
const json = '{ "name": "Foo", "description": "Bar" }';
const result = safeJsonParse(isMyType)(json) // result: ParseResult<MyType>
if (result.hasError) {
console.log("error :/") // further error handling here
} else {
console.log(result.parsed.description) // result.parsed now has type `MyType`
}
safeJsonParse pode ser estendido para falhar rapidamente ou JSON.parseerros de tentativa / captura .
Escrever funções de proteção de tipo manualmente torna-se complicado, se você precisar validar muitos valores diferentes. Existem bibliotecas para auxiliar nesta tarefa - exemplos (nenhuma lista abrangente):
io-ts: rel. popular (3,2 mil estrelas atualmente), fp-tsdependência de pares, estilo de programação funcionalzod: bastante novo (repo: 2020-03-07), se esforça para ser mais procedimental / orientado a objetos do queio-tstypescript-is: Transformador TS para API do compilador, wrapper adicional como ttypescript necessáriotypescript-json-schema/ ajv: Criar esquema JSON a partir de tipos e validá-lo comajvSe você deseja que seu JSON tenha um tipo Typecript validado, você mesmo precisará fazer esse trabalho de validação. Isso não é novidade. Em Javascript simples, você precisa fazer o mesmo.
Gosto de expressar minha lógica de validação como um conjunto de "transformações". Eu defino a Descriptorcomo um mapa de transformações:
type Descriptor<T> = {
[P in keyof T]: (v: any) => T[P];
};
Então, posso criar uma função que aplicará essas transformações a uma entrada arbitrária:
function pick<T>(v: any, d: Descriptor<T>): T {
const ret: any = {};
for (let key in d) {
try {
const val = d[key](v[key]);
if (typeof val !== "undefined") {
ret[key] = val;
}
} catch (err) {
const msg = err instanceof Error ? err.message : String(err);
throw new Error(`could not pick ${key}: ${msg}`);
}
}
return ret;
}
Agora, não estou apenas validando minha entrada JSON, mas estou construindo um tipo de escrita à medida que prossigo. Os tipos genéricos acima garantem que o resultado infere os tipos de suas "transformações".
No caso de a transformação gerar um erro (que é como você implementaria a validação), gostaria de envolvê-la com outro erro mostrando qual chave causou o erro.
Em seu exemplo, eu usaria isso da seguinte maneira:
const value = pick(JSON.parse('{"name": "Bob", "error": false}'), {
name: String,
error: Boolean,
});
Agora valueserão digitados, visto que Stringe Booleansão ambos "transformadores" no sentido de que recebem a entrada e retornam uma saída digitada.
Além disso, o valueserá realmente desse tipo. Em outras palavras, se namefosse realmente 123, ele será transformado para "123"para que você tenha uma string válida. Isso ocorre porque usamos Stringem tempo de execução, uma função integrada que aceita entrada arbitrária e retorna umstring .
Você pode ver isso funcionando aqui . Tente o seguinte para se convencer:
const valuedefinição para ver se o pop-over mostra o tipo correto."Bob"para 123e execute novamente a amostra. No console, você verá que o nome foi convertido corretamente para a string "123".namefosse realmente 123, ele será transformado para "123". Isso parece estar incorreto. Meu não valuevoltará quando eu copiar e colar todo o seu código exatamente e fizer essa alteração.{name: 123..{name:"123"..
123vez de "Bob").
Transformedtipo. Você pode apenas usar Object. type Descriptor<T extends Object> = { ... };
Transformedtipo é totalmente desnecessário. Eu atualizei a resposta de acordo.
Além disso, você pode usar bibliotecas que realizam validação de tipo de seu json, como Sparkson . Eles permitem que você defina uma classe TypeScript, para a qual você gostaria de analisar sua resposta, no seu caso poderia ser:
import { Field } from "sparkson";
class Response {
constructor(
@Field("name") public name: string,
@Field("error") public error: boolean
) {}
}
A biblioteca irá validar se os campos obrigatórios estão presentes na carga JSON e se seus tipos estão corretos. Ele também pode fazer várias validações e conversões.
Há uma ótima biblioteca para isso ts-json-object
No seu caso, você precisaria executar o seguinte código:
import {JSONObject, required} from 'ts-json-object'
class Response extends JSONObject {
@required
name: string;
@required
error: boolean;
}
let resp = new Response({"name": "Bob", "error": false});
Esta biblioteca irá validar o json antes de analisar
JSON.parse está disponível em TypeScript, então você pode apenas usá-lo:
JSON.parse('{"name": "Bob", "error": false}') // Returns a value of type 'any'
No entanto, você frequentemente desejará analisar um objeto JSON enquanto verifica se ele corresponde a um determinado tipo, em vez de lidar com um valor do tipo any. Nesse caso, você pode definir uma função como a seguinte:
function parse_json<TargetType extends Object>(
json: string,
type_definitions: { [Key in keyof TargetType]: (raw_value: any) => TargetType[Key] }
): TargetType {
const raw = JSON.parse(json);
const result: any = {};
for (const key in type_definitions) result[key] = type_definitions[key](raw[key]);
return result;
}
Essa função pega uma string JSON e um objeto contendo funções individuais que carregam cada campo do objeto que você está criando. Você pode usá-lo assim:
const value = parse_json(
'{"name": "Bob", "error": false}',
{ name: String, error: Boolean, }
);