Typescript-ifying modules #2538
Replies: 7 comments 15 replies
-
Thanks @smallsaucepan findPoint - I think returning undefined if nothing to return sounds goods. I wonder if there's a larger precedence of returning null? Have you seen this WIP PR #2275? I wonder if there's some relevant overlap, at least in usage of the *Each functions. I also wonder if it's worth landing this first, it was getting pretty close to ready. coordEach - I realize the behavior of returning a boolean is undocumented, but I believe I stumbled on it as well at some point and have utilized it as a way to make the function bail out, rather than proceed to iterate through all elements. I think that has value. Thoughts on how we can keep or still achieve that behavior? And I wonder if the |
Beta Was this translation helpful? Give feedback.
-
Thanks Tim. Had seen #2275. Wasn't sure its status though, noticing a few places where it seemed inconsistent. For example coordEach now returns Obviously though a work in progress - so perhaps this discussion can help us establish what we want to do, and then we can figure the best avenue to get there. I have the opposite opinion regarding early bail out, especially as the function mutates in place. Now I've potentially got a half processed piece of geojson with no idea at which point it stopped. Do you recall the use cases where that behaviour was an advantage? It might make more sense to keep Each functions predictable and add an additional category i.e. findFirst or coordEachUntil. The Reduce functions do use the Each functions, but define their own callback which doesn't interpret the result and isn't a way to abort the reduction. |
Beta Was this translation helpful? Give feedback.
-
Here's a first go at typing one of the reducers. Mind taking a look and letting me know if you can see any obstacles? This should cater for the situation where the type of return value we expect from reduce is totally different from the type of element we're processing e.g. // callbackfn simply appends all the coords together as strings.
const longUselessString = coordReduce<string>(geojson, callbackfn, ""); Not sure what happens in the above case though when we don't pass an initial value ... export function coordReduce<U>(
geojson: AllGeoJSON,
callback: (
accumulator: U,
currentCoord: Coord,
coordIndex: number,
featureIndex: number,
multiFeatureIndex: number,
geometryIndex: number
) => U,
initialValue?: U,
excludeWrapCoord?: boolean
): U {
let accumulator = initialValue;
coordEach(
geojson,
function (
currentCoord: Coord,
coordIndex: number,
featureIndex: number,
multiFeatureIndex: number,
geometryIndex: number
): void {
if (coordIndex === 0 && initialValue === undefined) {
// Initialise accumulator to "array[0]" and skip to second iteration.
accumulator = currentCoord as U;
} else {
// Cast accumulator to U as Typescript believes it could be
// U | undefined
// The if above should stop that from ever happening. A side effect
// of the way we're implementing reduce() with *Each.
accumulator = callback(
accumulator as U,
currentCoord,
coordIndex,
featureIndex,
multiFeatureIndex,
geometryIndex
);
}
},
excludeWrapCoord
);
// Same cast as above, as Typescript can't tell we'll have assigned
// "array[0]" to accumulator if initialValue was not provided.
return accumulator as U;
} |
Beta Was this translation helpful? Give feedback.
-
Is there a reason all the callback args for segmentReduce and lineReduce are optional? callback: (
previousValue?: Reducer,
currentSegment?: Feature<LineString, P>,
featureIndex?: number,
multiFeatureIndex?: number,
segmentIndex?: number,
geometryIndex?: number
) |
Beta Was this translation helpful? Give feedback.
-
Sorry just getting caught up. I think that we have code that uses null because undefined was not always a reserved keyword. You have already linked to #2275 but I've lost track of its current status. It must not be finished if there's inconsistencies that you spotted already. I think you're right though that returning false was intended to short-circuit the iteration and is probably important for performance in some places. @turf/meta isn't really intended to be used outside of our own packages IMO so I'm not super worried about changing behavior being a large burden to consumers. Having strongly typed @turf/helpers and @turf/meta seems pretty important to making sure that the rest of the Typescript packages can be written with good underlying safety. With a wider view, we still have a bunch of Javascript-only packages that could maybe be converted to get rid of the weird split build system. I didn't try to undertake the conversion a while ago because @rowanwins felt strongly that Typescript wasn't popular enough at the time and it may discourage contributions from the community. With the benefit of a little more hindsight in 2023 I wonder if we might be more comfortable moving entirely to Typescript now. |
Beta Was this translation helpful? Give feedback.
-
I find turf/meta functions very helpful as a heavy user of turf and consumer/producer of geojson. It lets me more easily build larger more complex spatial functions, that work with various types of spatial input. I think it's good that we expose them publicly. Granted I don't use them all but the *Each and *Reduce are probably the most useful. |
Beta Was this translation helpful? Give feedback.
-
After some thoughts on turf-shortest-path @DenisCarriere @mfedderly Docs say default resolution is 100. Looking at the code, resolution is actually calculated as the width of the bbox divided by 100. And then used to calculate the cell width and height. turf/packages/turf-shortest-path/index.js Lines 83 to 86 in c8b37e8 This works in the tests because resolution isn't passed, so the Question then, should the option name change - divisions maybe? - and the meaning of the resolution variable stay the same in the code. Or vice versa? Also if someone passes the minDistance option the code throws with "not implemented". It's been that way for 6 years. Should we just remove minDistance altogether? Found #2291 so maybe we thought it had already been done. |
Beta Was this translation helpful? Give feedback.
-
Converted so far:
completedin progress
turf-buffer
turf-differenceturf-dissolveturf-ellipseturf-envelopeturf-explodeturf-flattenturf-flipturf-geojson-rbush
turf-great-circle @thomas-hervey
turf-interpolate
turf-line-chunk
turf-line-offset
turf-line-slice-along
turf-line-slice
turf-line-split
turf-maskturf-meta
turf-midpointturf-planepointturf-point-on-featureturf-points-within-polygonturf-polygon-smoothturf-polygon-tangentsturf-rewindturf-sampleturf-sectorturf-shortest-pathturf-simplifyturf-squareturf-standard-deviational-ellipseturf-tagturf-tesselateturf-transform-rotateturf-transform-scaleturf-transform-translateturf-unkink-polygonturf-voronoiCurrently converting a few modules to Typescript. Some have been pretty straightforward (e.g. turf-flip) while others are looking a little more gnarly. Starting this discussion as a means to get some second opinions.
turf-meta
findPoint
findPoint is defined in index.d.ts as returning
Feature<Point, P>
though it pretty clearly also returns null. Options:Feature<Point, P> | null
Feature<Point, P> | undefined
and return undefined if the element requested doesn't existOption 2 seems best to me. I feel undefined implies non-existence more clearly than null. findSegment is similar.
coordEach
coordEach returns
void
as does the callback passed by the user. However, in many places we see this pattern:Typescript is stricter and rightly complains when we try to return boolean from a void function. Also, this would abort the traversal of all the elements as soon as the callback ever returned false. No mention of this in the docs so it would be very unexpected.
My gut feel is we strip out the logic and completely discard the result as the interface suggests. Any concerns with that? If anyone is checking the result of coordEach they're relying on something undocumented. Same would go for propEach, featuerEach, etc
Thoughts on this? Thanks
@mfedderly @JamesLMilner @twelch
Beta Was this translation helpful? Give feedback.
All reactions