Aylur / ags

A customizable and extensible shell

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

[TypeScript] Compilation fails with bind("volume") in Audio service

ryze312 opened this issue · comments

commented

TypeScript fails to compile when using binding on volume in Audio service

const audio = await Service.import("audio");
const widget = Widget.Label({
    label: audio.speaker.bind("volume").as((v) => v.toString())
});
src/windows/bar.ts:28:51 - error TS18046: 'v' is of type 'unknown'.

28     label: audio.speaker.bind("volume").as((v) => v.toString())
                                                     ~


Found 1 error in src/windows/bar.ts:28

VSCode LSP seems to infer the type correctly

image

Other fields work just fine, e.g is_muted

const audio = await Service.import("audio");
const widget = Widget.Label({
    label: audio.speaker.bind("is_muted").as((v) => `test: ${v}`)
});

I'm having trouble reproducing this in my setup. Could you specify which versions of ags and bun you're using, as well as the exact command you're using that prompts the typescript error?

commented

I am using AGS 1.8.2 and just running tsc, there is no Bun

Hmm. Still couldn't reproduce it with tsc (version 5.5.2). Is there something weird with your tsconfig.json? Also, what happens if you explicitly type v as a number (i.e. (v: number) => v.toString())?

commented

what happens if you explicitly type v as a number

error TS2345: Argument of type '(v: number) => string' is not assignable to parameter of type '(v: unknown) => string'.

Is there something weird with your tsconfig.json?

{
  "compilerOptions": {
    "target": "ES2022",
    "module": "ES2022",
    "moduleDetection": "force",
    "outDir": "build/target",
    "typeRoots": ["./types/*"],

    "strict": true,

    "exactOptionalPropertyTypes": true,
    "noImplicitReturns": true,
    "noImplicitOverride": true,
    "noImplicitAny": true,
    "noUnusedLocals": false,
    "noUnusedParameters": false,
    "noFallthroughCasesInSwitch": true,
    "noUncheckedIndexedAccess": true,
    "noPropertyAccessFromIndexSignature": true,
    "removeComments": true,

    // This is needed to prevent compile error on checking imports like gi://
    "skipLibCheck": true
  },

  // Include types manually
  "include": ["src/**/*", "types/**/*.d.ts"]
}

I have to include types manually because of #352

It looks like the other bindings are broken as well; the example of bind("is_muted").as((v) => 'test: ${v}') just doesn't exercise the failure case since unknown can be string interpolated just fine. If you use (v: boolean | null)... instead, it errors in the same way as with volume.

I'm inclined to think this is an issue with how TSC propagates/remembers types. For a minimal example, consider:

const i = audio.speaker.bind("is_muted");
const q: Binding<Stream, "is_muted", boolean | null> = i;

which breaks with a telling:

main.ts:50:5 - error TS2322: Type 'Binding<Stream, string, unknown>' is not assignable to type 'Binding<Stream, "is_muted", boolean | null>'.
  Type 'string' is not assignable to type '"is_muted"'.

50 var q: Binding<Stream, "is_muted", boolean | null> = i;

Note how the middle parameter has lost the specificity of keyof Stream. If, on the other hand, we explicitly constrain "is_muted", such as below:

const k: keyof Stream = "is_muted";
const i = audio.speaker.bind(k);
const q: Binding<Stream, "is_muted", boolean | null> = i;

it compiles just fine.

After further investigation into why other Services seem to be able to bind without issue, I've narrowed the culprit down to get stream(): Gvc.MixerStream | null; in types/service/audio.d.ts. The inclusion of that property yields the keyof Stream -> string translation that causes the observed compilation issues. Specifically, the | null aspect (which is consistent given that the Audio service works just fine with get control(): Gvc.MixerControl;).

TLDR: seems like this is probably either an issue with tsc's type system, or an issue with your tsconfig.json. I'm insufficiently familiar with tsc to judge one way or another. I'd recommend either living with a workaround or opening an issue on the tsc repo to see if someone there can debug further.

commented

Perhaps the keyof aspect is somewhat inconsistent in TypeScript, this might be a compiler bug. I'll see if I can come up with a minimal example to reproduce it and forward to TypeScript upstream.

commented

Looks like replacing the Props constraint with BindableProps here just fixes it.

ags/src/service.ts

Lines 105 to 107 in 8194f0c

bind<Prop extends keyof Props<this>>(prop: Prop) {
return new Binding(this, prop);
}

I am not sure if this was supposed to be used instead of Props in the first place or not. Could you possibly clarify @Aylur?

Looks like replacing the Props constraint with BindableProps here just fixes it.
the bind method of a Service takes one of its properties' name as a string, it can't take a Binding object

I am guessing your typescript version is out of date, there was another issue #332 where tsc failed

Can not reproduce. tsc -v is 5.4.5

any reason you want to transpile with tsc instead of using a bundler like esbuild or bun?

commented

I am guessing your typescript version is out of date
Can not reproduce. tsc -v is 5.4.5

I am running tsc 5.5.3

any reason you want to transpile with tsc instead of using a bundler like esbuild or bun?

I prefer using plain tsc