i hate typescript generics!! i hate typescript generics!!
This commit is contained in:
parent
62e9563b8a
commit
b735066f1c
4 changed files with 83 additions and 7 deletions
43
README.md
Normal file
43
README.md
Normal file
|
@ -0,0 +1,43 @@
|
|||
# serde-ts
|
||||
|
||||
a library for serializing and deserializing javascript objects
|
||||
|
||||
# Usage
|
||||
|
||||
this library makes no assumptions about formats and loosely follows the architecture of Rust's [serde](https://serde.rs/) crate. the major difference are the particular data types. serde-ts's internal data model contains the following types
|
||||
|
||||
## `serde-ts` data types
|
||||
* null/undefined
|
||||
* boolean
|
||||
* number
|
||||
* bigint
|
||||
* string
|
||||
* symbol
|
||||
* function
|
||||
* object
|
||||
* iterable
|
||||
* class
|
||||
|
||||
with the exception of iterables and classes, these types were chosen as they are the ones provided by Javascript's native `typeof` operator which is used internally for type checking.
|
||||
|
||||
there are four interfaces which are relevant to `serde-ts` users.
|
||||
|
||||
## `Serializer`
|
||||
|
||||
a `Serializer` is responsible for transforming the internal `serde-ts` data model into the target serialization format. this means that it will have a `serialize<Type>` function for each of `serde-ts`'s data types.
|
||||
|
||||
## `Deserializer`
|
||||
|
||||
a `Deserializer` is responsible for transforming the target serialization format into the internal `serde-ts` data model. it will have a `deserialize<Type>` for each of the `serde-ts`'s data types.
|
||||
|
||||
## `Serialize`
|
||||
|
||||
`Serialize` is responsible for transforming a value *to* the internal data model. for example, a `Vector2` may be internally serialized as an iterable of numbers.
|
||||
|
||||
## `Deserialize`
|
||||
|
||||
`Deserialize` is responsible for transforming a value *from* the internal data model. it is the inverse of `Serialize`. depending on the `Deserialize` target, it may accept an iterable of numbers and produce a `Vector2`.
|
||||
|
||||
|
||||
|
||||
|
|
@ -1,5 +1,6 @@
|
|||
import { ISerializeObject, ISerializer } from './interface'
|
||||
import { isPlainObject } from '../utils'
|
||||
import { ISerializeObject, ISerializer, Serialize } from './interface'
|
||||
import { isObject, isPlainObject, PrimitivePrototype } from '../utils'
|
||||
import { GlobalRegistry, Registry } from '../registry'
|
||||
|
||||
class UnhandledTypeError extends TypeError {
|
||||
constructor(serializer: ISerializer<unknown>, value: any) {
|
||||
|
@ -7,7 +8,7 @@ class UnhandledTypeError extends TypeError {
|
|||
}
|
||||
}
|
||||
|
||||
function serializeObject<T, V extends object, S extends ISerializeObject<T>>(serializer: S, obj: V): T {
|
||||
function serializeObject<T, U extends object, S extends ISerializeObject<T>>(serializer: S, obj: U): T {
|
||||
for (const key in obj) {
|
||||
const value = obj[key]
|
||||
serializer.serializeEntry(key, value)
|
||||
|
@ -16,13 +17,21 @@ function serializeObject<T, V extends object, S extends ISerializeObject<T>>(ser
|
|||
return serializer.end()
|
||||
}
|
||||
|
||||
function serializeClass<T, V extends object, S extends ISerializer<T>>(serializer: S, value: V): T {
|
||||
function serializeClass<T, U extends object, S extends ISerializer<T>>(serializer: S, value: U): T {
|
||||
const name = value.constructor.name
|
||||
const ser = serializer.serializeClass(name)
|
||||
return serializeObject(ser, value)
|
||||
}
|
||||
|
||||
export function serialize<T, V, S extends ISerializer<T>>(serializer: S, value: V): T {
|
||||
function getSerialize<T, U>(value: U, registry: Registry): Serialize<T> {
|
||||
if (isObject(value)) {
|
||||
return registry.serializers.get(value.constructor) as Serialize<T>
|
||||
}
|
||||
|
||||
return registry.serializers.get(PrimitivePrototype[typeof value]) || defaultSerialize as Serialize<T>
|
||||
}
|
||||
|
||||
function defaultSerialize<T, U, S extends ISerializer<T>>(serializer: S, value: U): T {
|
||||
switch (typeof value) {
|
||||
case 'string': return serializer.serializeString(value)
|
||||
case 'number': return serializer.serializeNumber(value)
|
||||
|
@ -40,3 +49,8 @@ export function serialize<T, V, S extends ISerializer<T>>(serializer: S, value:
|
|||
}
|
||||
}
|
||||
|
||||
export function serialize<T, U, S extends ISerializer<T>>(serializer: S, value: U, registry: Registry = GlobalRegistry): T {
|
||||
const ser = getSerialize<T, U>(value, registry)
|
||||
return ser(serializer, value)
|
||||
}
|
||||
|
||||
|
|
|
@ -77,6 +77,6 @@ export class Serializer<T> implements ISerializer<T> {
|
|||
}
|
||||
|
||||
export interface Serialize<T> {
|
||||
<U, S extends Serializer<U>>(serializer: S, value: T): U
|
||||
<U, S extends ISerializer<U>>(serializer: S, value: T): U
|
||||
}
|
||||
|
||||
|
|
21
src/utils.ts
21
src/utils.ts
|
@ -10,7 +10,11 @@ export interface ToString {
|
|||
toString(): string
|
||||
}
|
||||
|
||||
export function isPlainObject(value: any): boolean {
|
||||
export function isObject(value: any): value is object {
|
||||
return typeof value === 'object'
|
||||
}
|
||||
|
||||
export function isPlainObject(value: any): value is object {
|
||||
return Object.getPrototypeOf(value) === Object.prototype
|
||||
}
|
||||
|
||||
|
@ -42,3 +46,18 @@ export class IterResult {
|
|||
}
|
||||
}
|
||||
|
||||
export function Null(...args: any) {
|
||||
return null
|
||||
}
|
||||
|
||||
export const PrimitivePrototype = Object.freeze({
|
||||
undefined: Null,
|
||||
boolean: Boolean,
|
||||
number: Number,
|
||||
bigint: BigInt,
|
||||
string: String,
|
||||
symbol: Symbol,
|
||||
object: Object,
|
||||
function: Function
|
||||
} as const)
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue