2
\$\begingroup\$

I'd like you to take a look at those extension functions I made in kotlinjs (version 1.3.61):

inline fun <reified E : Element> Element.queryHtmlSelectorOrNull(selectors: String): E? {
    return this.querySelector(selectors = selectors) as E?
}

inline fun <reified E : Element> Element.queryHtmlSelector(selectors: String): E {
    return this.queryHtmlSelectorOrNull(selectors = selectors)
        ?: error("couldn't find element by selectors '$selectors'")
}

Now let me elaborate. I was using classic javascript querySelector. This signature:

fun querySelector(selectors: String): Element?

Has some downsides/qualities:

  • It is optional.
  • When I force cast to for example HTMLDivElement and result is actually null, I get NullPointerException with no additional information/error message. Example:

    document.querySelector(".something-non-existent") as HTMLDivElement //throws
    
  • I have to explicitly cast my query to specific element I want (example HTMLDivElement? or HTMLDivElement).

My functions fix some of the issues, but create new issues:

  • You can explicitly select what type you need or compiler can guess it based on variable type.

    Example:

    document.queryHtmlSelector<HTMLDivElement>(".something")
    

    or

    val element: HTMLDivElement = document.queryHtmlSelector(".something")
    

The downside is that this doesn't work (need to set type explicitly):

val element = document.queryHtmlSelector(".something")

I would be cool somehow to "default" base type to Element if none is specified, but can't think of way to do that.

I'd like something that can pretty much replace original function and just add benefits of stronger typing and nicer error message. Any hints on how to improve this are greatly appreciated.

\$\endgroup\$
3
  • \$\begingroup\$ Which Kotlin version are you using? \$\endgroup\$ Mar 28, 2020 at 17:32
  • \$\begingroup\$ Currently 1.3.61, but targeting latest. \$\endgroup\$
    – K.H.
    Mar 28, 2020 at 17:46
  • \$\begingroup\$ The usage of reified, do you have that to get any potential ClassCastExceptions earlier, or any other reason? \$\endgroup\$ Mar 30, 2020 at 14:44

1 Answer 1

2
\$\begingroup\$

"Would be cool somehow, to "default" base type to Element if none is specified, but can't think of way to do that." Indeed it is not possible to do that, at least not with the same method. An option could be to have a separate method for that. document.queryHtmlSelectorRaw ? Or wait, actually... That leaves you back to what you were starting with, Element.querySelector.

I'd like something, that can pretty much replace original function and just add benefits of stronger typing and nicer error message. Any hints on how to improve this are greatly appreciated.

Given those requirements, I'd say that the current methods is about as good as you're gonna get. You have the stronger typing (but you will have to specify it). You have the better error message if it's null.

The only things that I would consider are:

  • Get rid of reified, I'm not convinced that you need it.
  • Change method names, queryHtmlSelector can easily be confused with the built in querySelector. May I recommend just the name selector or select?
  • Improve the error message even further, such as No element found using selector '$selector' on $this.
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.