I'm finding a couple odd behaviors with workers. First, the debugger
call doesn't appear to break into the debugger on a worker thread. Using examples\base\worker
(using mcconfig -m -d -p win
), I simply added debugger;
to simpleworker.js
in onmessage
yet it does not break into the debugger.
let counter = 0
self.onmessage = function(msg) {
debugger;
self.postMessage({hello: "from simple worker", counter: ++counter});
}
Likewise, if I add a trace
call, the message does not appear.
let counter = 0
self.onmessage = function(msg) {
trace("Inside worker\n");
self.postMessage({hello: "from simple worker", counter: ++counter});
}
Are either of these limited to work only in the main thread and not a worker?
Timer
object is similar to setTimeout
and friends in the browser runtime. Maybe you can track down their declarations to see if that offers some insight. (They should be somewhere in the TypeScript repository on GitHub.)
It's a bit over my head ... but in node_modules/@types/node/timer.d.ts
is says:
declare module "timers" {
function setTimeout(callback: (...args: any[]) => void, ms: number, ...args: any[]): NodeJS.Timeout;
(... truncated ...)
It is also defined in globals.d.ts
as:
declare function setTimeout(callback: (...args: any[]) => void, ms: number, ...args: any[]): NodeJS.Timeout;
And also in globals.d.ts
it has:
declare namespace NodeJS {
(... truncated ...)
interface Timeout extends Timer {
hasRef(): boolean;
refresh(): this;
[Symbol.toPrimitive](): number;
}
(... truncated ...)
Perhaps NodeJS
namespace is the default namespace for global? In which case the interface is included there, rather than in the timer.d.ts
module, thereby making it globally available?
I'm afraid I don't follow all this... hoping this might be more helpful to you!
declare module "timer" {
type TimerCallback = (timer?: Timer) => void;
class Timer {
private constructor()
static set: (
callback: TimerCallback,
interval?: number,
repeat?: number
) => Timer
static repeat: (
callback: TimerCallback,
interval: number,
) => Timer
static schedule: (
timer: Timer,
interval?: number,
repeat?: number
) => void;
static clear: (timer: Timer) => void;
static delay: (milliseconds: number) => void;
private brand: boolean;
}
export {Timer as default};
}
function repeat(id: Timer)
{
trace(`repeat ${count} \n`);
if (5 == ++count)
Timer.clear(id);
}
Interesting ... so using the class of Timer
as both the class, and as the resulting identifier. I would have thought the result of the set()
method to be a unique type, not the Timer
class itself (such as the ts code above, which uses the type Timeout
). Perhaps because Timeout
just extends Timer
(according to the setTimeout
ts declaration) maybe this simplification is ok?
But given this knowledge, how does this help for example with Self
in Worker
(and I would guess other modules that have native types)?
Self
remains a single use global, for Worker
only, then it's not an issue. Maybe this is all just me trying to wrap my head around TypeScript! All is good right now - thanks as always for your willingness to share your time, and with a fantastic attitude to boot. I'm hoping the energy you are investing in me right now will pay back for you in the coming year... Happy New Year!
MyWorker
example, the class is only instantiated once per VM, so maybe it is enough to use some static methods? Since self is in the globals, it doesn't really need to be passed. So maybe something like this?class MyWorker {
static initialize(void): void {
self.onmessage = MyWorker.handleMessage;
}
static handleMessage(message:Object):void {
// do work...
}
}
MyWorker.initialize();
Serial
which appears to have Linux, Mac and Windows implementations, as well as ESP8266 (but maybe not ESP32, not sure). However, the IO manifest seems to support just ESP. Is this something I should ignore, or start to play with?
@phoddie I think I finally found what I was looking for in TypeScript ... types / interfaces / etc that you want available from a module, but without exporting/importing them by name, can be put into the global namespace. For example, here is serial.d.ts
for the new IO serial class:
declare module "builtin/serial" {
global {
type SerialConfiguration = {
device?: string,
baud?: number,
target?: any,
format?: string,
onError?(): void,
onReadable?(count:number): any,
onWritable?(count:number): any
};
type SerialSetOptions = {
RTS:boolean,
DTS:boolean
};
}
class Serial {
constructor(options: SerialConfiguration);
check(): void;
close(): void;
read(): ArrayBuffer | String;
write(data:ArrayBuffer | String): void;
set(options:SerialSetOptions):void;
get format():string;
set format(mode:string);
purge():void;
}
export {Serial as default};
}
This can then be used simply as:
import Serial from "builtin/serial";
And yet you can still directly use the types SerialConfiguration
and SerialSetOptions
, such as:
let config:SerialConfiguration = {
device: "COM3",
baud: 115200
};
Perhaps you knew this - but this is exactly what I was trying to find. A way to keep my types defined with the module, and contain multiple interfaces/types that can be used on primary class.
timer.d.ts1 might be, reverting to your prior version that used
TimerID` but now using the global namespace (including both TimerID and TimerCallback, as the callback also should be known externally):declare module "timer" {
global {
type TimerCallback = (timer?: TimerID) => void;
type TimerID = number;
}
class Timer {
private constructor()
static set: (
callback: TimerCallback,
interval?: number,
repeat?: number
) => TimerID
static repeat: (
callback: TimerCallback,
interval: number,
) => TimerID
static schedule: (
timer: TimerID,
interval?: number,
repeat?: number
) => void;
static clear: (timer: TimerID) => void;
static delay: (milliseconds: number) => void;
private brand: boolean;
}
export {Timer as default};
}
Scaled across many modules, that's going to be a lot of global types! Doesn't so much clutter in global confuse more than help? I know the web runtime includes a staggering number of globals. Maybe that is what is done for types too?
Setting that aside, a timer is an instance, not a number. Your proposed declaration allow any number to be passed as a TimerID. For example, this is not flagged as an error Timer.clear(12)
. The declaration I posted ensures that only a timer instance would be accepted as a parameter to the functions. (That's part of why the private brand is present in my version and is, I believe, unnecessary in yours.)
I'd have to look at the Timer module implementation to full grasp your point about instance vs. number - I was (I guess incorrectly) assuming it was implemented similar to setInterval
(which does return a number). If Timer.set
returns some instance (specifically, if it returns Timer
) then totally understood.
But as a more general point, if you look at the global types in the Typescript definition, you will find there are a ton, which generally makes sense as many types are global in nature. In the case of Timer
, even if Set
does return an instance of the Timer
object, then what about the type TimerCallback
? Without global, then something like TimerCallback would not be available for TS validation. There are many other examples of this challenge where a module needs to present multiple types/interfaces to correctly consume the module.
Further reading on this topic seems to indicate that namespaces can be used, but when they are system library services (as in this case with Timer
from moddable base) they seem to be in global
to avoid the extra mynamespace.mytype
syntax. Individual authors, especially library authors, may well want to use namespaces to avoid naming collisions - so one could argue that Moddable should have it's own namespace. I'm not a fan of this due to the extra required syntax (and it appears neither was NodeJS, as it seems to provide both global
and NodeJS
namespaces for typing... though I'm still not clear on what they are doing).
But do keep in mind... I'm a total novice at Typescript and no expert at Javascript. I have far more experience with more traditional OOP languages (C#, C++, Java, etc) and applying that experience to TS/JS isn't necessarily the right thing to do!
@cmidgley, even if a Timer was a number, that's an optimization: there's no practical reason to use it as a number. The declaration I wrote ensures a script can't accidentally pass a number as an argument that expects a Timer. That seems consistent with the overall goal of TypeScript.
Regarding the exporting a type for the callback, I tried to find a precedent. I looked to sort
on Array
. Here's the definition from TypeScript:
sort(compareFn?: (a: T, b: T) => number): this;
TypeScript does not declare a comparison function type. It repeats the above declaration on Array
and each TypedArray
(there are a lot!).
I hesitate to consider his a shortcoming in the TypeScript declarations. The TypeScript team could have declared a common type easily enough if that was considered important. I suspect it gets back to the fundamental differences between TypeScript and traditional strongly typed languages, like those you mentioned. Anyway, my goal is only to try to follow the common practices in TypeScript for our declarations. If there's a more appropriate precedent to follow, I'm all ears.
I feel like I missing something obvious here... I looked around and tried to find a better example of the problem I'm experiencing, which I hope is more clear here using WiFi
.
The following fragment using wifi.d.ts
works with valid type checking on WiFiOptions
(including detecting missing or invalid members):
import WiFi from "wifi";
let myWiFi:WiFi = new WiFi({ssid: "test"}, null);
Yet the following says "error TS2304: Cannot find name 'WiFiOptions'." on the second line:
import WiFi from "wifi";
let options:WiFiOptions = {ssid: "test"};
let myWiFi:WiFi = new WiFi(options, null);
How do I reference the type WiFiOptions
when it is not exported? It is known when I use it in the WiFi
constructor, but not as a separate type. Perhaps something is wrong in my build environment? Both VS Code intellisense and running mcconfig
generate the same error.
If I modify wifi.d.ts
to export type WiFiOptions
and then import with import WiFi, { WiFiOptions } from "wifi";
then it works fine but that means a lot of importing of types - which is what I was implying earlier, so perhaps I'm just doing this all wrong?
myWiFi
is of type WiFi
. Similarly, without declaring the type on options
if the value is incompatible, TypeScript will tell you that when you pass it to the WiFi
constructor. I understand that you don't get quite as strict checking, but... this seems to be how the TypeScript definitions for JavaScript work. Maybe that's their intent? What you are doing is trying to behave like C++ or Java. (I'm taking no position on what is better or correct. I don't have a strong opinion. I'm reflecting what I've taken from reviewing Microsoft's built-in declarations and reading their documentation. )
FWIW - I find this page on TypeScript's Type Compatibilty pretty helpful in understanding the big-picture approach and how it differs from strongly typed languages like C++ and Java. On the Modules page there's a brief note on Importing types which seems very relevant to what you are doing. It allows a module to export a type in a way that is explicitly independent from the JavaScript exports. I think that's relevant here for a couple reasons. First, by keeping the types export separate from the JavaScript language exports, there's no suggestion that they are part of the JavaScript API. Secondly, they are imported from the module rather than falling into the abyss of the giant global namespace. That brief text links to a longer description which explains this much better than I can here.
So.... maybe the solution is to export the types from the modules (such as WiFiOptions
above). Code that wants to explicitly declare variables with those types can import them explicitly. Code that does not want to be that explicit with types, can omit the import of the types. That keeps the simple case simple while allowing for the behavior that (I think I understand) you want.
Closer? ;)
x-ios
and x-ios-simulator
, but no implementation of the platform itself. Is this something under consideration, or perhaps it hit some roadblock (like Apple's ios limitations on allowing scripting languages)? My use case is to embed it into a ios app (actually, in Unity) so I can share the scripts. It's not critical, as I can also run it in a server and remote the interface but wanted to see if you had any insight before proceeding with the design.
mcconfig
sources. During our sojourn at Marvell we shipped mobile products for both. They worked really well -- lighter and faster than just about anything. But, there are many, many ways to build apps for mobile. The opening for one more is limited. Our focus is microcontrollers.