#Types for "isPojo" and "isIterable"

100 messages · Page 1 of 1 (latest)

tribal umbra
#

hello,

I have two functions, isPojo returns true if value is pojo (its's constructor is "Object"), I have return type set to:

value is { [key: PropertyKey]: *}

but it's incorrect,because it gives type "true" for non Pojos, such as arrays or Sets.

Another function isIterable returns true if value is iterable (contain "Symbol.iterator") but is not string. I have return type set to value is Iterable, but this includes string.

Can someone help me, please ?

upbeat ferry
#

do you really need those types to work at compile time?

#

sounds like something that should be checked at runtime only

#

also, since TS is a structural type language, there is no difference between a POJO and a class instance as long as the types are concenrned

#

also, Symbol.iterator means it's some kind of collection or generator
and strings are none of theose things
you can't really loop on a string anyway, you're looping over the characters making up that string, not the string itself

tribal umbra
#

yes, I need it at build time... it's for my transpiler. I need for typescript to tell me that type is pojo or someting else inside if-else when passing Pojo or Array or Set....

upbeat ferry
#

oh, so when you mean pojo, you just mean "object" or "record", as "not an array" or "not a function"

#

not "object", as "not an instance"

tribal umbra
#

I mean:

isPojo({}) // true
isPojo([]) // false
isPojo(new Set()) // false
isPojo(new Date()) // false

upbeat ferry
#

but what about isPojo(new Object())?

#

or

class Foo {}

isPojo(new Foo());
tribal umbra
upbeat ferry
#

ok, so you mean "record"

#

(we don't use the term POJO in JS/TS)

#

or maybe even "dictionary"

harsh tundraBOT
#
Ascor8522#7606

Preview:ts function isObject( element: unknown ): element is Record<any, any> { return ( typeof element === "object" && element !== null && element.constructor.name === "Object" ) }

tribal umbra
#

it gives me true for [] value.... using TS 5.0.0-dev.20230226

upbeat ferry
#

right, arrays are objects technically

#

with their keys being numbers instead of strings

#

just ass && !(element instanceof Array)

tribal umbra
#

yes, they are...

tribal umbra
upbeat ferry
#

you are still checking for the constructor name

#

so it will still exclude all those classes

tribal umbra
#

btw, I tried to exclude just array,and it throws error '}' expected. ts(1005)

upbeat ferry
upbeat ferry
tribal umbra
#

yes ,that works fine in runtime ,but types that VSCode shows are incorrect,and if I use:

if (isPojo(value)) {
value;
// ^ any[] | { name: string }
}

#

but it should show only any[] there

upbeat ferry
#

if the function returns true, value is of type { [key: any]: any } (same as Record<any, any>)

#

so check your entier code

tribal umbra
#

function is correct (implementation / runtime), I already tested it, but it's types are incorrect

upbeat ferry
#

can you share the whole code?

tribal umbra
#
/**
 * @param {*} value
 * @returns {value is {
 *     [key: PropertyKey]: *
 * }}
 */
export function isPojo(value) {
    if (value === null && typeof value !== 'object') return false;
    const proto = Object.getPrototypeOf(value);
    if (proto === null) return true;
    return proto.constructor.name === 'Object';
}

I'm writing on mobile that code manually so sorry for typos, but code is practically that.

#

it's actually based on implementation from mongoose

upbeat ferry
#

that's nowhere near the code I showed

#

you ned the : value is Record<any, any>

tribal umbra
#

yes ,I tried that too ,didn't work

upbeat ferry
#

wdym, didn't work?

#

show more than just your isPojo function

upbeat ferry
tribal umbra
#

I tried both value is Record<*, *> and value is { [key: PropertyKey]: * }

upbeat ferry
#

* is not a valid TS type

#

use any

#

as I showed

tribal umbra
#

in JSDoc * is any... but even with any it shows any[] inside if body,when I use isPojo check

upbeat ferry
#

...

#

don't use JSDoc

#

JSDoc are comments

#

they don't do anything

#

you are working with TS

#

you need to specify the TS return type

tribal umbra
#

They do ,.... JSDoc is normally used for type checking... even Typescript has it in tutorials

upbeat ferry
#

like so function isPojo(value: unknown): value is Record<any, any>

sand flicker
#

JSDoc can be used in .js files to provide typings, yeah.

upbeat ferry
sand flicker
#

I'm pretty sure there's a syntax for type predicates.

upbeat ferry
#

also, why bothering with JSDoc when the doc can be extracted from the types

tribal umbra
sand flicker
#

(I personally do recommend using .ts files unless there's a strong reason to use .js + JSDoc, since the JSDoc syntax is a lot more annoying, but it's a tangent)

tribal umbra
#

I will try to move type to .d.ts if it works

#

nope ,doesn't work in .d.ts file too

#

nor ts...

upbeat ferry
#

.d.ts files are files to add typed for the exported function of your library for you consumers

#

they are not made to type your internal code

#

just use a TS file with proper types and everything should work

tribal umbra
#

as I said, in .ts it doesn't work either.

#

I copied your code from playground and got any[] inside if body

upbeat ferry
#

well

#

let's prentend value was for type any[] | "foo"

#

it went through the check, so now we know it's an object, it can't be of type "foo"

#

and since arrays are objects, any[] still validates our constraint

#

so the final type for value after the check is any[]

harsh tundraBOT
#
Mlocik97#8200

Preview:```ts
function isObject(
element: any
): element is Record<any, any> {
return (
typeof element === "object" &&
element !== null &&
element.constructor.name === "Object"
)
}

const val: string[] = []

if (isObject(val)) {
val
// ^
} e
...```

sand flicker
#

What behavior were you expecting there?

#

A string array is a valid Record<any, any> so that seems correct to me.

tribal umbra
sand flicker
#

I don't think there's a way to represent "constructor.name equal to object" at a type level so I'm not sure you should be trying to write a type-guard for it.

upbeat ferry
sand flicker
#

I think "POJO" really isn't a particularly meaningful idea in the Typescript type system.

sand flicker
#

If you wanted to do it not as a type-guard, I guess you could:

function isObject(element: any): element is object {
    return element && typeof element === "object" || typeof element === "function";
}

function isPOJO(element: object) { // Not a type-guard because this isn't something that can be expressed in the type-system
    return element.constructor.name === "Object"
}

function example(val: unknown) {
    if(isObject(val) && isPOJO(val)) {
        // val is object here
    }
}
upbeat ferry
#

that's the first thing I asked, does OP really need narrowing / exact types?

#

like, can't a simple runtime check just be good enough

upbeat ferry
tribal umbra
#

:/ ok, it's sad, but whatever,... I will creare Feature Request on Github.

upbeat ferry
tribal umbra
upbeat ferry
#

at runtime

#

using .constructor

#

like you did

#

but TS doesn't care about the prototype of an object
as long as the final shape is the same, they are the "same" object

tribal umbra
#

ok, let's forget about isPojo it seems that's not really doable,... but can we make isIterable works the way, it returns true for predicate if value is Iterable but not string...

e.g.

value is Iterable & !string

but this doesn't work... is there some isnot operator?

#

I tried value is Iterable<T extends string ? never : T> but doesn't work.

#

value is Exclude<Iterable, string> doesn't work... 😦

upbeat ferry
#

but this doesn't work... is there some isnot operator?
no, there isn't

#

if you are unsure, just have a look at the docs, don't try to guess by trying random things