refactor(struct): simplify how StructInitType was computed

also allows multiple variable length `arraybuffer` fields to use same `lengthField`
This commit is contained in:
Simon Chan 2021-01-14 11:59:48 +08:00
parent f3a921857a
commit 5f0a6a8808
27 changed files with 188 additions and 141 deletions

View file

@ -49,7 +49,7 @@ will install lerna locally and bootstrap all packages.
### Scripts
* `npm run build`: build `@yume-chan/event`, `@yume-chan/adb` and `@yume-chan/adb-backend-web` packages.
* `npm run build:watch`: build and watch changes for `@yume-chan/event`, `@yume-chan/adb` and `@yume-chan/adb-backend-web` packages.
* `npm run build`: build all npm packages.
* `npm run build:watch`: build and watch changes for all npm packages.
* `npm run start:demo`: start webpack-dev-server for the `demo` package.
* `npm run build:demo`: build the `demo` package.

View file

@ -1,4 +1,4 @@
import { Struct, StructValueType } from "@yume-chan/struct";
import Struct from "@yume-chan/struct";
import { Adb } from '../adb';
import { AdbBufferedStream } from '../stream';
@ -35,7 +35,7 @@ export const AdbFrameBufferV1 =
.uint32('alpha_length')
.uint8ClampedArray('data', { lengthField: 'size' });
export type AdbFrameBufferV1 = StructValueType<typeof AdbFrameBufferV1>;
export type AdbFrameBufferV1 = typeof AdbFrameBufferV1['deserializedType'];
export const AdbFrameBufferV2 =
new Struct({ littleEndian: true })
@ -54,7 +54,7 @@ export const AdbFrameBufferV2 =
.uint32('alpha_length')
.uint8ClampedArray('data', { lengthField: 'size' });
export type AdbFrameBufferV2 = StructValueType<typeof AdbFrameBufferV2>;
export type AdbFrameBufferV2 = typeof AdbFrameBufferV2['deserializedType'];
export async function framebuffer(adb: Adb) {
const socket = await adb.createSocket('framebuffer:');

View file

@ -1,5 +1,5 @@
import { AutoDisposable } from '@yume-chan/event';
import { Struct } from '@yume-chan/struct';
import Struct from '@yume-chan/struct';
import { AdbPacket } from '../packet';
import { AdbIncomingSocketEventArgs, AdbPacketDispatcher, AdbSocket } from '../socket';
import { AdbBufferedStream } from '../stream';

View file

@ -1,4 +1,4 @@
import { Struct, StructValueType } from '@yume-chan/struct';
import Struct from '@yume-chan/struct';
import { AdbBufferedStream } from '../../stream';
import { AdbSyncRequestId, adbSyncWriteRequest } from './request';
import { AdbSyncDoneResponse, adbSyncReadResponse, AdbSyncResponseId } from './response';
@ -11,7 +11,7 @@ export const AdbSyncEntryResponse =
.string('name', { lengthField: 'nameLength' })
.extra({ id: AdbSyncResponseId.Entry as const });
export type AdbSyncEntryResponse = StructValueType<typeof AdbSyncEntryResponse>;
export type AdbSyncEntryResponse = typeof AdbSyncEntryResponse['deserializedType'];
const ResponseTypes = {
[AdbSyncResponseId.Entry]: AdbSyncEntryResponse,

View file

@ -1,4 +1,4 @@
import { Struct } from '@yume-chan/struct';
import Struct from '@yume-chan/struct';
import { AdbBufferedStream } from '../../stream';
import { AdbSyncRequestId, adbSyncWriteRequest } from './request';
import { AdbSyncDoneResponse, adbSyncReadResponse, AdbSyncResponseId } from './response';

View file

@ -1,4 +1,4 @@
import { Struct } from '@yume-chan/struct';
import Struct from '@yume-chan/struct';
import { AdbBufferedStream } from '../../stream';
import { chunkArrayLike, chunkAsyncIterable } from '../../utils';
import { AdbSyncRequestId, adbSyncWriteRequest } from './request';

View file

@ -1,4 +1,4 @@
import { Struct } from '@yume-chan/struct';
import Struct from '@yume-chan/struct';
import { AdbBufferedStream } from '../../stream';
export enum AdbSyncRequestId {

View file

@ -1,4 +1,4 @@
import { Struct, StructDeserializationContext, StructValueType } from '@yume-chan/struct';
import Struct, { StructDeserializationContext, StructLike, StructValueType } from '@yume-chan/struct';
import { AdbBufferedStream } from '../../stream';
export enum AdbSyncResponseId {
@ -16,7 +16,7 @@ export enum AdbSyncResponseId {
// For example DONE responses for LIST requests are 16 bytes (same as DENT responses),
// but DONE responses for STAT requests are 12 bytes (same as STAT responses)
// So we need to know responses' size in advance.
export class AdbSyncDoneResponse {
export class AdbSyncDoneResponse implements StructLike<AdbSyncDoneResponse> {
private length: number;
public readonly id = AdbSyncResponseId.Done;
@ -39,7 +39,7 @@ export const AdbSyncFailResponse =
throw new Error(object.message);
});
export async function adbSyncReadResponse<T extends Record<string, { deserialize(context: StructDeserializationContext): Promise<any>; }>>(
export async function adbSyncReadResponse<T extends Record<string, StructLike<any>>>(
stream: AdbBufferedStream,
types: T,
): Promise<StructValueType<T[keyof T]>> {

View file

@ -1,4 +1,4 @@
import { placeholder, Struct, StructValueType } from '@yume-chan/struct';
import Struct, { placeholder } from '@yume-chan/struct';
import { AdbBufferedStream } from '../../stream';
import { AdbSyncRequestId, adbSyncWriteRequest } from './request';
import { adbSyncReadResponse, AdbSyncResponseId } from './response';
@ -29,7 +29,7 @@ export const AdbSyncLstatResponse =
}
});
export type AdbSyncLstatResponse = StructValueType<typeof AdbSyncLstatResponse>;
export type AdbSyncLstatResponse = typeof AdbSyncLstatResponse['deserializedType'];
export enum AdbSyncStatErrorCode {
EACCES = 13,
@ -78,7 +78,7 @@ export const AdbSyncStatResponse =
}
});
export type AdbSyncStatResponse = StructValueType<typeof AdbSyncStatResponse>;
export type AdbSyncStatResponse = typeof AdbSyncStatResponse['deserializedType'];
const StatResponseType = {
[AdbSyncResponseId.Stat]: AdbSyncStatResponse,

View file

@ -1,4 +1,4 @@
import { Struct, StructInitType, StructValueType } from '@yume-chan/struct';
import Struct from '@yume-chan/struct';
import { AdbBackend } from './backend';
import { BufferedStream } from './stream';
@ -25,9 +25,9 @@ const AdbPacketStruct =
.fields(AdbPacketHeader)
.arrayBuffer('payload', { lengthField: 'payloadLength' });
export type AdbPacket = StructValueType<typeof AdbPacketStruct>;
export type AdbPacket = typeof AdbPacketStruct['deserializedType'];
export type AdbPacketInit = Omit<StructInitType<typeof AdbPacketStruct>, 'checksum' | 'magic'>;
export type AdbPacketInit = Omit<typeof AdbPacketStruct['initType'], 'checksum' | 'magic'>;
export namespace AdbPacket {
export function create(

View file

@ -769,7 +769,17 @@
"@yume-chan/async-operation-manager": {
"version": "2.1.3",
"resolved": "https://registry.npmjs.org/@yume-chan/async-operation-manager/-/async-operation-manager-2.1.3.tgz",
"integrity": "sha512-YUGrCXEADvaM1tJgQ0ek/a5eSg0ovdpL5ZhfXS6ZRnV3Xn0q5y/j/xzl+yBFFQ+nWclWh8of5f8Qm8y/y0YdQQ=="
"integrity": "sha512-YUGrCXEADvaM1tJgQ0ek/a5eSg0ovdpL5ZhfXS6ZRnV3Xn0q5y/j/xzl+yBFFQ+nWclWh8of5f8Qm8y/y0YdQQ==",
"requires": {
"tslib": "2.0.3"
},
"dependencies": {
"tslib": {
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.0.3.tgz",
"integrity": "sha512-uZtkfKblCEQtZKBF6EBXVZeQNl82yqtDQdv+eck8u7tdPxjLu2/lp5/uPW+um2tpuxINHWy3GhiccY7QgEaVHQ=="
}
}
},
"abab": {
"version": "2.0.5",

View file

@ -1,7 +1,7 @@
import { Adb, AdbBufferedStream, AdbSocket, DataEventEmitter, EventQueue } from '@yume-chan/adb';
import { PromiseResolver } from '@yume-chan/async-operation-manager';
import { DisposableList, EventEmitter } from '@yume-chan/event';
import { Struct, StructValueType } from '@yume-chan/struct';
import Struct from '@yume-chan/struct';
import { AndroidCodecLevel, AndroidCodecProfile } from './codec';
import { AndroidKeyEventAction, AndroidMotionEventAction, ScrcpyControlMessageType, ScrcpyInjectKeyCodeControlMessage, ScrcpyInjectTextControlMessage, ScrcpyInjectTouchControlMessage, ScrcpySimpleControlMessage } from './message';
import { parse_sequence_parameter_set } from './sps';
@ -159,7 +159,7 @@ const VideoPacket =
export const NoPts = BigInt(-1);
export type VideoPacket = StructValueType<typeof VideoPacket>;
export type VideoPacket = typeof VideoPacket['deserializedType'];
const ClipboardMessage =
new Struct()

View file

@ -1,4 +1,4 @@
import { placeholder, Struct, StructInitType } from '@yume-chan/struct';
import Struct, { placeholder } from '@yume-chan/struct';
export enum ScrcpyControlMessageType {
InjectKeycode,
@ -18,7 +18,7 @@ export const ScrcpySimpleControlMessage =
new Struct()
.uint8('type', placeholder<ScrcpyControlMessageType.BackOrScreenOn>());
export type ScrcpySimpleControlMessage = StructInitType<typeof ScrcpySimpleControlMessage>;
export type ScrcpySimpleControlMessage = typeof ScrcpySimpleControlMessage['initType'];
export enum AndroidMotionEventAction {
Down,
@ -48,7 +48,7 @@ export const ScrcpyInjectTouchControlMessage =
.uint16('pressure')
.uint32('buttons');
export type ScrcpyInjectTouchControlMessage = StructInitType<typeof ScrcpyInjectTouchControlMessage>;
export type ScrcpyInjectTouchControlMessage = typeof ScrcpyInjectTouchControlMessage['initType'];
export const ScrcpyInjectTextControlMessage =
new Struct()
@ -57,7 +57,7 @@ export const ScrcpyInjectTextControlMessage =
.string('text', { lengthField: 'length' });
export type ScrcpyInjectTextControlMessage =
StructInitType<typeof ScrcpyInjectTextControlMessage>;
typeof ScrcpyInjectTextControlMessage['initType'];
export enum AndroidKeyEventAction {
Down = 0,
@ -106,7 +106,7 @@ export const ScrcpyInjectKeyCodeControlMessage =
.uint32('metaState');
export type ScrcpyInjectKeyCodeControlMessage =
StructInitType<typeof ScrcpyInjectKeyCodeControlMessage>;
typeof ScrcpyInjectKeyCodeControlMessage['initType'];
export type ScrcpyControlMessage =
ScrcpySimpleControlMessage |

10
packages/event/.npmignore Normal file
View file

@ -0,0 +1,10 @@
.github
.vscode
coverage
src/**.spec.ts
jest.config.js
pnpm-lock.yaml
renovate.json
tsconfig.json
*.tsbuildinfo

View file

@ -1,21 +0,0 @@
MIT License
Copyright (c) 2020 Simon Chan
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View file

@ -0,0 +1,4 @@
module.exports = {
preset: 'ts-jest',
testEnvironment: 'node',
};

View file

@ -1,10 +1,11 @@
{
"name": "@yume-chan/event",
"private": true,
"version": "0.0.4",
"description": "Event/EventEmitter",
"keywords": [
"event"
"event",
"eventemitter",
"typescript"
],
"license": "MIT",
"author": {
@ -27,6 +28,7 @@
"scripts": {
"build": "rimraf {cjs,esm,dts,*.tsbuildinfo} && tsc -b tsconfig.esm.json tsconfig.cjs.json",
"build:watch": "tsc -b -w tsconfig.esm.json tsconfig.cjs.json",
"test": "jest",
"coverage": "jest --coverage",
"prepublishOnly": "npm run build"
},

View file

@ -0,0 +1,21 @@
import { AutoDisposable, Disposable } from './disposable';
describe('Event', () => {
describe('AutoDisposable', () => {
it('should dispose its dependencies', () => {
const myDisposable = {
dispose: jest.fn(),
};
class MyAutoDisposable extends AutoDisposable {
constructor(disposable: Disposable) {
super();
this.addDisposable(disposable);
}
}
const myAutoDisposable = new MyAutoDisposable(myDisposable);
myAutoDisposable.dispose();
expect(myDisposable.dispose).toBeCalledTimes(1);
});
});
});

View file

@ -581,7 +581,7 @@ All above built-in methods are alias of `field`. To add a field of a custom type
### `FieldDefinition`
```ts
abstract class FieldDefinition<TOptions = void, TValueType = unknown, TRemoveFields = never> {
abstract class FieldDefinition<TOptions = void, TValueType = unknown, TOmitInit = never> {
readonly options: TOptions;
constructor(options: TOptions);

View file

@ -4,6 +4,8 @@
"description": "C-style structure serializer and deserializer.",
"keywords": [
"structure",
"serialization",
"deserialization",
"typescript"
],
"license": "MIT",

View file

@ -16,12 +16,12 @@ type ValueOrPromise<T> = T | Promise<T>;
*
* @template TOptions TypeScript type of this definition's `options`.
* @template TValueType TypeScript type of this field.
* @template TRemoveInitFields Optional remove some field from the `TInit` type. Should be a union of string literal types.
* @template TOmitInit Optionally remove some fields from the init type. Should be a union of string literal types.
*/
export abstract class FieldDefinition<
TOptions = void,
TValueType = unknown,
TRemoveInitFields = never,
TOmitInit = never,
> {
public readonly options: TOptions;
@ -33,9 +33,9 @@ export abstract class FieldDefinition<
/**
* When `T` is a type initiated `FieldDefinition`,
* use `T['removeInitFields']` to retrieve its `TRemoveInitFields` type parameter.
* use `T['omitInitType']` to retrieve its `TOmitInit` type parameter.
*/
public readonly removeInitFields!: TRemoveInitFields;
public readonly omitInitType!: TOmitInit;
public constructor(options: TOptions) {
this.options = options;
@ -55,7 +55,7 @@ export abstract class FieldDefinition<
options: Readonly<StructOptions>,
context: StructDeserializationContext,
object: any,
): ValueOrPromise<FieldRuntimeValue<FieldDefinition<TOptions, TValueType, TRemoveInitFields>>>;
): ValueOrPromise<FieldRuntimeValue<FieldDefinition<TOptions, TValueType, TOmitInit>>>;
/**
* When implemented in derived classes, creates a `FieldRuntimeValue` from a given `value`.
@ -65,5 +65,5 @@ export abstract class FieldDefinition<
context: StructSerializationContext,
object: any,
value: TValueType,
): FieldRuntimeValue<FieldDefinition<TOptions, TValueType, TRemoveInitFields>>;
): FieldRuntimeValue<FieldDefinition<TOptions, TValueType, TOmitInit>>;
}

View file

@ -0,0 +1,9 @@
import Struct from './index';
describe('Struct', () => {
describe("Index", () => {
it('should export default Struct', () => {
expect(Struct).toBeDefined();
});
});
});

View file

@ -1,4 +1,4 @@
export * from './basic';
export * from './struct';
export { default as Struct } from './struct';
export { Struct as default } from './struct';
export * from './types';

View file

@ -1,26 +1,26 @@
import { createRuntimeObject, FieldDefinition, FieldRuntimeValue, getRuntimeValue, setRuntimeValue, StructDefaultOptions, StructDeserializationContext, StructOptions, StructSerializationContext } from './basic';
import { ArrayBufferFieldType, ArrayBufferLikeFieldType, Evaluate, FixedLengthArrayBufferLikeFieldDefinition, FixedLengthArrayBufferLikeFieldOptions, Identity, KeysOfType, NumberFieldDefinition, NumberFieldType, Overwrite, StringFieldType, Uint8ClampedArrayFieldType, VariableLengthArrayBufferLikeFieldDefinition, VariableLengthArrayBufferLikeFieldOptions } from './types';
export interface StructLike<TValue> {
deserialize(context: StructDeserializationContext): Promise<TValue>;
}
export type Awaited<T> = T extends Promise<infer R> ? Awaited<R> : T;
/**
* Extract the value type of the specified `Struct`
*
* The lack of generic constraint is on purpose to allow `StructLike` types
*/
export type StructValueType<T> =
T extends { deserialize(context: StructDeserializationContext): Promise<infer R>; } ? R : never;
/**
* Extract the init type of the specified `Struct`
*/
export type StructInitType<T extends Struct<any, object, object, any>> =
T extends { create(value: infer R, ...args: any): any; } ? Evaluate<R> : never;
export type StructValueType<T extends StructLike<any>> =
Awaited<ReturnType<T['deserialize']>>;
/**
* Create a new `Struct` type with `TDescriptor` appended
*/
type AddFieldDescriptor<
TValue extends object,
TInit extends object,
TFields extends object,
TOmitInit extends string,
TExtra extends object,
TPostDeserialized,
TFieldName extends PropertyKey,
@ -28,10 +28,9 @@ type AddFieldDescriptor<
Identity<Struct<
// Merge two types
// Evaluate immediately to optimize editor hover tooltip
Evaluate<TValue & Record<TFieldName, TDefinition['valueType']>>,
// There is no `Evaluate` here, because otherwise the type of a `Struct` with many fields
// can become too complex for TypeScript to compute
Evaluate<Omit<TInit, TDefinition['removeInitFields']> & Record<TFieldName, TDefinition['valueType']>>,
Evaluate<TFields & Record<TFieldName, TDefinition['valueType']>>,
// Merge two `TOmitInit
TOmitInit | TDefinition['omitInitType'],
TExtra,
TPostDeserialized
>>;
@ -40,8 +39,8 @@ type AddFieldDescriptor<
* Overload methods to add an array buffer like field
*/
interface ArrayBufferLikeFieldCreator<
TValue extends object,
TInit extends object,
TFields extends object,
TOmitInit extends string,
TExtra extends object,
TPostDeserialized
> {
@ -64,8 +63,8 @@ interface ArrayBufferLikeFieldCreator<
options: FixedLengthArrayBufferLikeFieldOptions,
typescriptType?: TTypeScriptType,
): AddFieldDescriptor<
TValue,
TInit,
TFields,
TOmitInit,
TExtra,
TPostDeserialized,
TName,
@ -81,7 +80,7 @@ interface ArrayBufferLikeFieldCreator<
<
TName extends PropertyKey,
TType extends ArrayBufferLikeFieldType,
TOptions extends VariableLengthArrayBufferLikeFieldOptions<TInit>,
TOptions extends VariableLengthArrayBufferLikeFieldOptions<TFields>,
TTypeScriptType = TType['valueType'],
>(
name: TName,
@ -89,8 +88,8 @@ interface ArrayBufferLikeFieldCreator<
options: TOptions,
typescriptType?: TTypeScriptType,
): AddFieldDescriptor<
TValue,
TInit,
TFields,
TOmitInit,
TExtra,
TPostDeserialized,
TName,
@ -105,8 +104,8 @@ interface ArrayBufferLikeFieldCreator<
* Similar to `ArrayBufferLikeFieldCreator`, but bind to a `ArrayBufferLikeFieldType`
*/
interface ArrayBufferTypeFieldDefinitionCreator<
TValue extends object,
TInit extends object,
TFields extends object,
TOmitInit extends string,
TExtra extends object,
TPostDeserialized,
TType extends ArrayBufferLikeFieldType
@ -119,8 +118,8 @@ interface ArrayBufferTypeFieldDefinitionCreator<
options: FixedLengthArrayBufferLikeFieldOptions,
typescriptType?: TTypeScriptType,
): AddFieldDescriptor<
TValue,
TInit,
TFields,
TOmitInit,
TExtra,
TPostDeserialized,
TName,
@ -132,16 +131,16 @@ interface ArrayBufferTypeFieldDefinitionCreator<
<
TName extends PropertyKey,
TLengthField extends KeysOfType<TInit, number | string>,
TOptions extends VariableLengthArrayBufferLikeFieldOptions<TInit, TLengthField>,
TLengthField extends KeysOfType<TFields, number | string>,
TOptions extends VariableLengthArrayBufferLikeFieldOptions<TFields, TLengthField>,
TTypeScriptType = TType['valueType'],
>(
name: TName,
options: TOptions,
typescriptType?: TTypeScriptType,
): AddFieldDescriptor<
TValue,
TInit,
TFields,
TOmitInit,
TExtra,
TPostDeserialized,
TName,
@ -155,18 +154,25 @@ interface ArrayBufferTypeFieldDefinitionCreator<
export type StructPostDeserialized<TValue, TPostDeserialized> =
(this: TValue, object: TValue) => TPostDeserialized;
export default class Struct<
TValue extends object = {},
TInit extends object = {},
export type StructDeserializedType<TFields extends object, TExtra extends object, TPostDeserialized> =
TPostDeserialized extends undefined ? Overwrite<TExtra, TFields> : TPostDeserialized;
export class Struct<
TFields extends object = {},
TOmitInit extends string = never,
TExtra extends object = {},
TPostDeserialized = undefined,
> {
public readonly valueType!: TValue;
> implements StructLike<StructDeserializedType<TFields, TExtra, TPostDeserialized>>{
public readonly fieldsType!: TFields;
public readonly initType!: TInit;
public readonly omitInitType!: TOmitInit;
public readonly extraType!: TExtra;
public readonly initType!: Evaluate<Omit<TFields, TOmitInit>>;
public readonly deserializedType!: StructDeserializedType<TFields, TExtra, TPostDeserialized>;
public readonly options: Readonly<StructOptions>;
private _size = 0;
@ -179,7 +185,7 @@ export default class Struct<
private _extra: PropertyDescriptorMap = {};
private _postDeserialized?: StructPostDeserialized<TValue, any>;
private _postDeserialized?: StructPostDeserialized<any, any>;
public constructor(options?: Partial<StructOptions>) {
this.options = { ...StructDefaultOptions, ...options };
@ -195,8 +201,8 @@ export default class Struct<
name: TName,
definition: TDefinition,
): AddFieldDescriptor<
TValue,
TInit,
TFields,
TOmitInit,
TExtra,
TPostDeserialized,
TName,
@ -217,8 +223,8 @@ export default class Struct<
public fields<TOther extends Struct<any, any, any, any>>(
other: TOther
): Struct<
TValue & TOther['valueType'],
TInit & TOther['initType'],
TFields & TOther['fieldsType'],
TOmitInit | TOther['omitInitType'],
TExtra & TOther['extraType'],
TPostDeserialized
> {
@ -385,27 +391,32 @@ export default class Struct<
);
}
private arrayBufferLike: ArrayBufferLikeFieldCreator<TValue, TInit, TExtra, TPostDeserialized> = (
private arrayBufferLike: ArrayBufferLikeFieldCreator<
TFields,
TOmitInit,
TExtra,
TPostDeserialized
> = (
name: PropertyKey,
type: ArrayBufferLikeFieldType,
options: FixedLengthArrayBufferLikeFieldOptions | VariableLengthArrayBufferLikeFieldOptions
): any => {
if ('length' in options) {
return this.field(
name,
new FixedLengthArrayBufferLikeFieldDefinition(type, options),
);
} else {
return this.field(
name,
new VariableLengthArrayBufferLikeFieldDefinition(type, options),
);
}
};
if ('length' in options) {
return this.field(
name,
new FixedLengthArrayBufferLikeFieldDefinition(type, options),
);
} else {
return this.field(
name,
new VariableLengthArrayBufferLikeFieldDefinition(type, options),
);
}
};
public arrayBuffer: ArrayBufferTypeFieldDefinitionCreator<
TValue,
TInit,
TFields,
TOmitInit,
TExtra,
TPostDeserialized,
ArrayBufferFieldType
@ -417,8 +428,8 @@ export default class Struct<
};
public uint8ClampedArray: ArrayBufferTypeFieldDefinitionCreator<
TValue,
TInit,
TFields,
TOmitInit,
TExtra,
TPostDeserialized,
Uint8ClampedArrayFieldType
@ -430,8 +441,8 @@ export default class Struct<
};
public string: ArrayBufferTypeFieldDefinitionCreator<
TValue,
TInit,
TFields,
TOmitInit,
TExtra,
TPostDeserialized,
StringFieldType
@ -456,14 +467,14 @@ export default class Struct<
// This trick disallows any keys that are already in `TValue`
Exclude<
keyof T,
Exclude<keyof T, keyof TValue>
Exclude<keyof T, keyof TFields>
>,
never
>>(
value: T & ThisType<Overwrite<Overwrite<TExtra, T>, TValue>>
value: T & ThisType<Overwrite<Overwrite<TExtra, T>, TFields>>
): Struct<
TValue,
TInit,
TFields,
TOmitInit,
Overwrite<TExtra, T>,
TPostDeserialized
> {
@ -478,8 +489,8 @@ export default class Struct<
* will also change the return type of `deserialize` to `never`.
*/
public postDeserialize(
callback: StructPostDeserialized<TValue, never>
): Struct<TValue, TInit, TExtra, never>;
callback: StructPostDeserialized<TFields, never>
): Struct<TFields, TOmitInit, TExtra, never>;
/**
* Registers (or replaces) a custom callback to be run after deserialized.
*
@ -487,8 +498,8 @@ export default class Struct<
* (or doesn't modify it at all), so `deserialize` will still return the result object.
*/
public postDeserialize(
callback?: StructPostDeserialized<TValue, void>
): Struct<TValue, TInit, TExtra, undefined>;
callback?: StructPostDeserialized<TFields, void>
): Struct<TFields, TOmitInit, TExtra, undefined>;
/**
* Registers (or replaces) a custom callback to be run after deserialized.
*
@ -496,10 +507,10 @@ export default class Struct<
* will `deserialize` to return that object instead.
*/
public postDeserialize<TPostSerialize>(
callback?: StructPostDeserialized<TValue, TPostSerialize>
): Struct<TValue, TInit, TExtra, TPostSerialize>;
callback?: StructPostDeserialized<TFields, TPostSerialize>
): Struct<TFields, TOmitInit, TExtra, TPostSerialize>;
public postDeserialize(
callback?: StructPostDeserialized<TValue, any>
callback?: StructPostDeserialized<TFields, any>
) {
this._postDeserialized = callback;
return this as any;
@ -511,7 +522,7 @@ export default class Struct<
return object;
}
public create(init: TInit, context: StructSerializationContext): Overwrite<TExtra, TValue> {
public create(init: Evaluate<Omit<TFields, TOmitInit>>, context: StructSerializationContext): Overwrite<TExtra, TFields> {
const object = this.initializeObject();
for (const [name, definition] of this._fields) {
@ -524,7 +535,7 @@ export default class Struct<
public async deserialize(
context: StructDeserializationContext
): Promise<TPostDeserialized extends undefined ? Overwrite<TExtra, TValue> : TPostDeserialized> {
): Promise<StructDeserializedType<TFields, TExtra, TPostDeserialized>> {
const object = this.initializeObject();
for (const [name, definition] of this._fields) {
@ -533,7 +544,7 @@ export default class Struct<
}
if (this._postDeserialized) {
const result = this._postDeserialized.call(object as TValue, object as TValue);
const result = this._postDeserialized.call(object as TFields, object as TFields);
if (result) {
return result;
}
@ -542,7 +553,7 @@ export default class Struct<
return object as any;
}
public serialize(init: TInit, context: StructSerializationContext): ArrayBuffer {
public serialize(init: Evaluate<Omit<TFields, TOmitInit>>, context: StructSerializationContext): ArrayBuffer {
const object = this.create(init, context) as any;
let structSize = 0;

View file

@ -102,11 +102,11 @@ const EmptyArrayBuffer = new ArrayBuffer(0);
export abstract class ArrayBufferLikeFieldDefinition<
TType extends ArrayBufferLikeFieldType = ArrayBufferLikeFieldType,
TOptions = void,
TRemoveInitFields = never,
TOmitInit = never,
> extends FieldDefinition<
TOptions,
TType['valueType'],
TRemoveInitFields
TOmitInit
>{
public readonly type: TType;
@ -123,7 +123,7 @@ export abstract class ArrayBufferLikeFieldDefinition<
options: Readonly<StructOptions>,
context: StructDeserializationContext,
object: any,
): Promise<ArrayBufferLikeFieldRuntimeValue<ArrayBufferLikeFieldDefinition<TType, TOptions, TRemoveInitFields>>> {
): Promise<ArrayBufferLikeFieldRuntimeValue<ArrayBufferLikeFieldDefinition<TType, TOptions, TOmitInit>>> {
const size = this.getDeserializeSize(object);
let arrayBuffer: ArrayBuffer;
@ -147,7 +147,7 @@ export abstract class ArrayBufferLikeFieldDefinition<
context: StructSerializationContext,
object: any,
value: TType['valueType'],
): ArrayBufferLikeFieldRuntimeValue<ArrayBufferLikeFieldDefinition<TType, TOptions, TRemoveInitFields>>;
): ArrayBufferLikeFieldRuntimeValue<ArrayBufferLikeFieldDefinition<TType, TOptions, TOmitInit>>;
}
export class ArrayBufferLikeFieldRuntimeValue<

View file

@ -47,8 +47,7 @@ export class NumberFieldDefinition<
TTypeScriptType = TType['valueType'],
> extends FieldDefinition<
void,
TTypeScriptType,
never
TTypeScriptType
> {
public readonly type: TType;

View file

@ -3,8 +3,8 @@ import { ArrayBufferLikeFieldDefinition, ArrayBufferLikeFieldRuntimeValue, Array
import { KeysOfType } from './utils';
export interface VariableLengthArrayBufferLikeFieldOptions<
TInit = object,
TLengthField extends KeysOfType<TInit, number | string> = any,
TFields = object,
TLengthField extends KeysOfType<TFields, number | string> = any,
> {
lengthField: TLengthField;
}