Reputation: 8693
I am trying to convert https://github.com/camwiegert/typical/blob/master/typical.js into TypeScript.
In Vanilla JS, it looks like:
export async function type(node, ...args) {
for (const arg of args) {
switch (typeof arg) {
case 'string':
await edit(node, arg);
break;
case 'number':
await wait(arg);
break;
case 'function':
await arg(node, ...args);
break;
default:
await arg;
}
}
}
async function edit(node, text) {
const overlap = getOverlap(node.textContent, text);
await perform(node, [...deleter(node.textContent, overlap), ...writer(text, overlap)]);
}
async function wait(ms) {
await new Promise(resolve => setTimeout(resolve, ms));
}
async function perform(node, edits, speed = 60) {
for (const op of editor(edits)) {
op(node);
await wait(speed + speed * (Math.random() - 0.5));
}
}
export function* editor(edits) {
for (const edit of edits) {
yield (node) => requestAnimationFrame(() => node.textContent = edit);
}
}
export function* writer([...text], startIndex = 0, endIndex = text.length) {
while (startIndex < endIndex) {
yield text.slice(0, ++startIndex).join('');
}
}
export function* deleter([...text], startIndex = 0, endIndex = text.length) {
while (endIndex > startIndex) {
yield text.slice(0, --endIndex).join('');
}
}
export function getOverlap(start, [...end]) {
return [...start, NaN].findIndex((char, i) => end[i] !== char);
}
And my TS conversion looks like:
export async function type(node: HTMLElement, ...args: any[]): Promise<void> {
for (const arg of args) {
switch (typeof arg) {
case 'string':
await edit(node, arg);
break;
case 'number':
await wait(arg);
break;
case 'function':
await arg(node, ...args);
break;
default:
await arg;
}
}
}
async function edit(node: HTMLElement, text: string): Promise<void> {
const overlap = getOverlap(node.textContent, text);
await perform(node, [
...deleter(node.textContent, overlap),
...writer(text, overlap),
]);
}
async function wait(ms: number): Promise<void> {
await new Promise(resolve => setTimeout(resolve, ms));
}
async function perform(
node: HTMLElement,
edits: Iterable<string | null>,
speed: number = 60
): Promise<void> {
for (const op of editor(edits)) {
op(node);
await wait(speed + speed * (Math.random() - 0.5));
}
}
export function* editor(
edits: Iterable<string | null>
): Generator<(node: any) => number, void, unknown> {
for (const edit of edits) {
yield node => requestAnimationFrame(() => (node.textContent = edit));
}
}
export function* writer(
[...text]: string,
startIndex: number = 0,
endIndex: number = text.length
): Generator<string, void, unknown> {
while (startIndex < endIndex) {
yield text.slice(0, ++startIndex).join('');
}
}
export function* deleter(
[...text]: string | null,
startIndex: number = 0,
endIndex: number = text.length
): Generator<string, void, unknown> {
while (endIndex > startIndex) {
yield text.slice(0, --endIndex).join('');
}
}
export function getOverlap(start: any, [...end]: Iterable<any>): number {
return [...start, NaN].findIndex((char, i) => end[i] !== char);
}
Mostly, I followed VSCode's advice on hover to type it & some logic.
However, it gives me an error saying:
typical.ts(61,3): semantic error TS2488: Type 'string | null' must have a 'Symbol.iterator' method that returns an iterator.
Line 61 is deleter()
function's [...text]
, i.e,:
[...text]: string | null,
How do I solve this?
Upvotes: 2
Views: 4394
Reputation: 1227
So, [...text]
argument from function deleter
is actually an array of characters.
for example, consider this call deleter(node.textContent, overlap)
.
type of node.textContent
can be string
or null
and the argument type is [...text]: string | null
but textContent
can be null
and you cannot destructure it to [...text]
. There will be two paths.
[...text] = node.textContent
[...text] = null
The second one is invalid that's why you got this type error. You may change the function to
export function* deleter(
text: string | null,
startIndex: number = 0
): Generator<string, void, unknown> {
if (text === null) {
yield ""// Or throw
return
}
const [...chars] = text
let endIndex = chars.length
while (endIndex > startIndex) {
yield chars.slice(0, --endIndex).join("");
}
}
Update 1:
Path was expected to have a container!
error can be resolved by adding curly braces {...}
around the switch case body.
Like.
case 'string':
{
await edit(node, arg);
}
break;
unknown Statement of type "ForOfStatement"
error can be resolve by adding correct babel configs.
Here is the correct config.
//.babelrc.js
module.exports = {
"presets": [
[
"@babel/env",
{
"targets": {
"browsers": "last 2 Chrome versions",
"node": "current"
}
}
]
],
"plugins": [
"@babel/plugin-transform-for-of",
['@babel/plugin-transform-runtime',
{
helpers: false
}
],
"@babel/plugin-transform-destructuring"
]
}
Upvotes: 3