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,
optional,
record,
string,
} from "tiny-decoders";
interface User {
name: string;
age: number;
active: boolean;
id: string | number;
}
const verifyUser = (decoder: (value: unknown) => User): User =>
decoder(undefined);
const userDecoder: (value: unknown) => User = autoRecord({
name: string,
age: number,
active: boolean,
id: either(string, number),
});
verifyUser(userDecoder);
const userDecoder2: (value: unknown) => User = record(field => ({
name: field("name", string),
age: field("age", number),
active: field("active", boolean),
id: field("id", either(string, number)),
}));
verifyUser(userDecoder2);
: shortcut,
{ ...EMPTY_SHORTCUT }
);
}
export type KeyboardMapping = {
shortcut: Shortcut,
action: KeyboardAction,
};
export const decodeKeyboardMapping: Decoder = autoRecord({
shortcut: decodeShortcut,
action: map(string, decodeKeyboardAction),
});
export const decodeKeyboardMappingWithModifiers: Decoder = autoRecord(
{
shortcut: map(decodeShortcut, requireModifier),
action: map(string, decodeKeyboardAction),
}
);
export type KeyboardModeBackground =
| { type: "FromHintsState" }
| { type: "PreventOverTyping", sinceTimestamp: number }
| { type: "Capture" };
export type KeyboardModeWorker =
| "Normal"
| "Hints"
| "PreventOverTyping"
| "Capture";
numVisibleElements: number,
numVisibleFrames: number,
bailed: number,
durations: decodeDurations,
});
export type Perf = Array<{
timeToFirstPaint: number,
timeToLastPaint: number,
topDurations: Durations,
collectStats: Array,
renderDurations: Durations,
}>;
export const decodePerf: Decoder = array(
autoRecord({
timeToFirstPaint: number,
timeToLastPaint: number,
topDurations: decodeDurations,
collectStats: array(decodeStats),
renderDurations: decodeDurations,
})
);
export type TabsPerf = { [tabId: string]: Perf, ... };
export const decodeTabsPerf: Decoder = dict(decodePerf);
export class TimeTracker {
_durations: Durations = [];
_current: ?{ label: string, timestamp: number } = undefined;
return;
}
this.hasRestoredPosition = true;
const recordProps = {
expandedPerfTabIds: optional(
map(array(string), ids =>
ids.filter(id => ({}.hasOwnProperty.call(this.state.perf, id)))
),
([]: Array)
),
expandedPerf: optional(boolean, false),
expandedDebug: optional(boolean, false),
scrollY: optional(number, 0),
};
const data = await browser.storage.local.get(Object.keys(recordProps));
const decoder = autoRecord(recordProps);
const { scrollY, expandedPerfTabIds, ...state } = decoder(data);
this.setState({ ...state, expandedPerfTabIds }, () => {
window.scrollTo(0, scrollY);
});
}
}