Secure your code as it's written. Use Snyk Code to scan source code in minutes - no build needed - and fix issues immediately.
return `${personA.name} meets ${personB.name}`;
}
meet(personDecoder9(testPerson), personDecoder9Auto(testPerson));
// If it feels like you are specifying everything twice – once in a `type` or
// `interface`, and once in the decoder – you might find this `ReturnType`
// technique interesting. If annotating your decoders like shown earlier in this
// file (`record((field): MyType => ({...}))` and `autoRecord({...})`),
// TypeScript will make sure that your type definition and decoders stay in
// sync, so there’s little room for error there. But with the `ReturnType`
// approach you don’t have to write what your records look like “twice.”
// Personally I don’t mind the “duplication,” but if you do – try out the
// `ReturnType` approach!
// Here’s a more complex example for trying out TypeScript’s inference.
const userDecoder = autoRecord({
id: either(string, number),
name: string,
age: number,
active: boolean,
country: optional(string),
type: constant("user"),
});
// Let TypeScript infer the `User` type:
type User = ReturnType;
// Try hovering over `User` in the line above – your editor should reveal the
// exact shape of the type.
const data = {
id: 1,
name: "John Doe",
ctrl: boolean,
// If missing it means that the shift key doesn’t matter. For example, it
// doesn’t matter if you need to press shift to type a `/` or not (which
// differs between keyboard layouts).
shift: ?boolean,
};
export type Shortcut = {
key: string,
alt: boolean,
cmd: boolean,
ctrl: boolean,
shift: boolean,
};
const decodeShortcut: Decoder = autoRecord({
key: string,
alt: boolean,
cmd: boolean,
ctrl: boolean,
shift: boolean,
});
const EMPTY_SHORTCUT: Shortcut = {
key: "",
alt: false,
cmd: false,
ctrl: false,
shift: false,
};
function requireModifier(shortcut: Shortcut): Shortcut {
import { Decoder, autoRecord, map, number, string } from "tiny-decoders";
interface Camel {
firstName: string;
age: number;
}
const verifyCamel = (decoder: Decoder): Camel => decoder(undefined);
// Successful rename (approach 1):
verifyCamel(
map(
autoRecord({
first_name: string,
age: number,
}),
({ first_name: firstName, ...rest }) => ({ firstName, ...rest })
)
);
// Didn’t remove "first_name":
// Unlike Flow, TypeScript does not have exact types. Returning an object with
// extraneous properties is OK.
verifyCamel(
map(
autoRecord({
first_name: string,
age: number,
}),
// https://github.com/microsoft/TypeScript/issues/18020
/*
* MAKING A TYPE FROM THE DECODER
*/
// Rather than first typing out an `interface` for `Person` and then essentially
// typing the same thing again in the decoder (especially `autoRecord` decoders
// look almost identical to `interface` they decode to!), you can start with the
// decoder and extract the type afterwards with TypeScript’s `ReturnType`
// utility.
const personDecoder9 = record(field => ({
name: field("name", string),
age: field("age", number),
}));
const personDecoder9Auto = autoRecord({
name: string,
age: number,
});
// $ExpectType { name: string; age: number; }
type Person2 = ReturnType;
// $ExpectType { name: string; age: number; }
type Person3 = ReturnType;
greet(personDecoder9(testPerson));
greet(personDecoder9Auto(testPerson));
function meet(personA: Person2, personB: Person3): string {
return `${personA.name} meets ${personB.name}`;
}
meet(personDecoder9(testPerson), personDecoder9Auto(testPerson));
// If it feels like you are specifying everything twice – once in a `type` or
// `interface`, and once in the decoder – you might find this `ReturnType`
}));
const personDecoder6Auto: Decoder = autoRecord({
name: string,
age: number,
extra: string,
});
greet(personDecoder6(testPerson));
greet(personDecoder6Auto(testPerson));
// The shorter notation does produce an error for `autoRecord`, but not for `record`.
const personDecoder7 = record(field => ({
name: field("name", string),
age: field("age", number),
extra: field("extra", string),
}));
const personDecoder7Auto = autoRecord({
name: string,
age: number,
// $ExpectError
extra: string,
// ^^^^^^^^^^
// Argument of type '{ name: (value: unknown) => string; age: (value: unknown) => number; extra: (value: unknown) => string; }' is not assignable to parameter of type '{ name: Decoder; age: Decoder; }'.
// Object literal may only specify known properties, and 'extra' does not exist in type '{ name: Decoder; age: Decoder; }'. ts(2345)
});
greet(personDecoder7(testPerson));
greet(personDecoder7Auto(testPerson));
// Luckily, the last type annotation style for `record` does produce an error!
const personDecoder8 = record(
(field): Person => ({
name: field("name", string),
age: field("age", number),
// Object literal may only specify known properties, and 'aye' does not exist in type 'Person'. ts(2322)
})
);
greet(personDecoder4(testPerson));
/*
* EXTRA PROPERTY
*/
// TypeScript allows passing extra properties, so without type annotations there are no errors:
const personDecoder5 = record(field => ({
name: field("name", string),
age: field("age", number),
extra: field("extra", string),
}));
const personDecoder5Auto = autoRecord({
name: string,
age: number,
extra: string,
});
greet(personDecoder5(testPerson));
greet(personDecoder5Auto(testPerson));
// Adding `Decoder` does not seem to help TypeScript find any errors:
const personDecoder6: Decoder = record(field => ({
name: field("name", string),
age: field("age", number),
extra: field("extra", string),
}));
const personDecoder6Auto: Decoder = autoRecord({
name: string,
age: number,
};
case "UpdateElements":
return {
type: "UpdateElements",
token: "",
viewports: field("viewports", decodeViewports),
};
default:
throw fieldError("type", `Unknown FrameMessage type: ${repr(type)}`);
}
}
);
const decodeViewports: Decoder> = array(
autoRecord({
// A viewport of a frame can be partially off-screen.
x: number,
y: number,
width: decodeUnsignedFloat,
height: decodeUnsignedFloat,
})
);
// $ExpectType { readonly [key: string]: unknown; }
mixedDict(undefined);
// $ExpectType true
constant(true)(undefined);
// $ExpectType false
constant(false)(undefined);
// $ExpectType 0
constant(0)(undefined);
// $ExpectType "const"
constant("const")(undefined);
// $ExpectType undefined
constant(undefined)(undefined);
// $ExpectType null
constant(null)(undefined);
// $ExpectType string[]
array(string)(undefined);
// $ExpectType { [key: string]: string; }
dict(string)(undefined);
// $ExpectType string
record(() => "")(undefined);
// $ExpectType { a: string; }
record(field => ({ a: field("a", string) }))(undefined);
// $ExpectType string
tuple(() => "")(undefined);
// $ExpectType string[]
tuple(item => [item(0, string)])(undefined);
// $ExpectType [string]
tuple<[string]>(item => [item(0, string)])(undefined);
// $ExpectType [string, boolean]
pair(string, boolean)(undefined);
// $ExpectType [string, boolean, boolean]
triple(string, boolean, boolean)(undefined);
name: field("name", string),
// $ExpectError
aye: field("age", number),
// ^^^^^^^^^^^^^^^^^^^^^^
// Type '{ name: string; aye: number; }' is not assignable to type 'Person'.
// Object literal may only specify known properties, and 'aye' does not exist in type 'Person'. ts(2322)
})
);
greet(personDecoder4(testPerson));
/*
* EXTRA PROPERTY
*/
// TypeScript allows passing extra properties, so without type annotations there are no errors:
const personDecoder5 = record(field => ({
name: field("name", string),
age: field("age", number),
extra: field("extra", string),
}));
const personDecoder5Auto = autoRecord({
name: string,
age: number,
extra: string,
});
greet(personDecoder5(testPerson));
greet(personDecoder5Auto(testPerson));
// Adding `Decoder` does not seem to help TypeScript find any errors:
const personDecoder6: Decoder = record(field => ({
name: field("name", string),
age: field("age", number),
active: true,
country: undefined,
// $ExpectError
type: "nope",
// ^^^^^^^^^
// Type '"nope"' is not assignable to type '"user"'. ts(2322)
};
/*
* MAKING A TYPE FROM THE DECODER – CAVEATS
*/
// Let’s say we need to support two types of users – anonymous and registered ones.
// Unfortunately, TypeScript doesn’t infer the type you might have expected:
// $ExpectType Decoder<{ type: string; sessionId: number; id?: undefined; name?: undefined; } | { type: string; id: number; name: string; sessionId?: undefined; }>
const userDecoder3 = record((field, fieldError) => {
const type = field("type", string);
switch (type) {
case "anonymous":
return {
type: "anonymous",
sessionId: field("sessionId", number),
};
case "registered":
return {
type: "registered",
id: field("id", number),
name: field("name", string),
};