Search code examples
javascripttypescriptpropertieses6-proxy

Create a construct that evaluates to a string and can have other strings as properties


I'd like to make particular JavaScript construct, with appropriate TypeScript typings

I want my construct to:

  1. return a string when evaluated
  2. have properties that return different strings when evaluated

in a similar fashion to how you can make nested callable functions:

const fn = () => "foo"
fn.bar = () => "bar"

fn() // "foo"
fn.bar() // "bar"

something like

const foo = "foo";
foo.bar = "bar" // Unfortunately doesn't work

foo // "foo"
foo.bar // "bar" 

type Foo = "foo" & { bar: "bar" }

It could also be a property tied to a parent Proxy object, if necessary. When I tried implementing it with a Proxy, I ran into the problem of not being able to differentiate shallow (proxy.foo) and deep (proxy.foo.bar) property access from the get trap:

const proxy = new Proxy({}, { 
  get: (target, prop) => {
    // prop is "foo" for both `proxy.foo` and `proxy.foo.bar` accesses, 
    // presumably because they are two separate property access operations 
    // `proxy.foo.bar` = `(proxy.foo).bar`
  }
}

is it even possible to make such construct in JavaScript?
And is possible to type it in TS?


Edit: Describing my use case

I want this to create a schema that maps semantic sections of my website to their respective urls:

const urlSchema = someMagic();

urlSchema.settings // `/settings`
urlSchema.settings.personal // `/settings/personal`

I then want to import this schema from throughout the app to create links etc.


Solution

  • I want my construct to return a string when evaluated and have properties

    That is not possible. An expression that evaluates to a string returns a string value, and a string is a primitive value not an object that could have properties. A proxy won't help here since a proxy is always an object as well.

    See also Extend primitives without prototyping them.