Solution suggested in the comments:
// gl-matrix
type vec3 = [number, number, number] | Float32Array;
type ReadonlyVec3 = readonly [number, number, number] | Float32Array;
// three.js
type Vector3 = {x:number, y:number, z:number}
interface Thing{
setPosition(p:ReadonlyVec3): void
getPosition():ReadonlyVec3
}
class ThreeJsThing implements Thing{
private position:Vector3 = {x:0, y:0, z:0}
setPosition(p:ReadonlyVec3){
// actually this.position.set(p[0], p[1], p[2])
this.position.x = p[0]
this.position.y = p[1]
this.position.z = p[2]
}
getPosition(){
// actually return vec3.fromValues(...) as const
return [
this.position.x,
this.position.y,
this.position.z
] as const
}
}
let thing = new ThreeJsThing()
let position = [1, 2, 3] as vec3
thing.setPosition(position)
console.log(thing.getPosition())
console.log(thing.getPosition()[1])
// position = thing.getPosition() // Type 'readonly [...]' is not assignable to type 'vec3'.
// thing.getPosition()[1] = 5 // Cannot assign to '1' because it is a read-only property.
Here is another solution that was posted by KLASANGUI, got downvoted into oblivion and then deleted. Use with caution, it probably opens a whole nother can of worms and blows up at three more places but I consider it absolutely valid and a beautiful JavaScript flex. It completely solves every problem I stated without syntactic sugar compromises.
// gl-matrix
type vec3 = [number, number, number] | Float32Array;
type ReadonlyVec3 = readonly [number, number, number] | Float32Array;
// three.js
type Vector3 = { x: number, y: number, z: number }
interface Thing {
position: vec3
}
class ThreeJsThing implements Thing {
#position: Vector3 = { x: 0, y: 0, z: 0 }
set position(p: vec3) {
this.#position.x = p[0]
this.#position.y = p[1]
this.#position.z = p[2]
}
get position() {
let p = this.#position
const accessor = {
length: 3,
set 0(x:number) { p.x = x },
get 0() { return p.x },
set 1(y:number) { p.y = y },
get 1() { return p.y },
set 2(z:number) { p.z = z },
get 2() { return p.z }
};
Object.setPrototypeOf(accessor, Array.prototype);
return accessor as vec3
}
}
let thing = new ThreeJsThing()
let position = [1, 2, 3] as vec3
thing.position = position
thing.position[1] = 5
position = thing.position
console.log(position)