Skip to content

Conversation

@bigmistqke
Copy link

Hi @toji 👋

I came across a bug in maplibre-gl that was due to a cast to make the types work nicely with gl-matrix.

It made me look at gl-matrix to see if I could improve its types. I saw some pain points and made an implementation to address these.

These are the changes I made:

  • Infer return type of gl-matrix methods from its inputs
  • Add overridable type for typing glMatrix.ARRAY_TYPE

Infer return type of gl-matrix methods from its inputs

const vector = vec3.clone([], vec3.create());

In the snippet above vector is typed as vec3 or IndexedCollection | [number, number, number], while it can safely be inferred that the result will be [number,number,number] based on its inputs.

I introduced two type-only namespaces Tuple and ReturnType.

  • Tuple collects all of the related tuples: Tuple.Vec3
  • ReturnType collects all of the return types of the gl-matrix methods: ReturnType.Vec3<T>
    • it returns the respective tuple if an array is passed, else it returns its template

This way we can return as narrow as possible:

// typed as `[number, number, number]`
const tupleVec3 = vec3.clone([], vec3.create()); 

 // typed as `Float32Array`
const float32ArrayVec3 = vec3.clone(new Float32Array(), vec3.create());

sadly typescript does not provide built-in support for defining the length of a typed array type (e.g. Float32Array<{ length: 3 }>) otherwise we could provide even narrower support.

Add overridable type for typing glMatrix.ARRAY_TYPE

Several gl-matrix methods create an array type based on the variable glMatrix.ARRAY_TYPE, for example vec3.create(). These functions currently return their respective wide type: for example vec3.create() returns vec3 or [number, number, number] | IndexedCollection.

glMatrix.ARRAY_TYPE is by default set to typeof Float32Array !== "undefined" ? Float32Array : Array. This can be overwritten by the user, but there is no way for them to control the type.

I implemented this via module augmentation.

declare module "gl-matrix" { 
  interface Overrides { 
    ArrayType: Float64Array 
  } 
}

// Will be typed as `Float64Array`
const vector = vec3.create()

Conclusion

By implementing these changes and testing them out in the maplibre-gl codebase I was able to reveal several dodgy types, so I think there is merit to the code.

Open for discussion/feedback/...

- Add generic templates to gl-matrix methods to infer return types based on input
- Introduce ReturnType namespace that narrows Array<number> to Tuple types: e.g. `const tuple = mat4.clone([], …)`
- Support overridable ArrayType via module augmentation
- Change return types from generic mat*/vec* to ArrayType for create/clone functions
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant