A
arktype•12mo ago
Micha

Why scope().compile().infer resolves to any?

I tried to follow the example here: https://arktype.io/docs/scopes Not sure if this is a bug or if I'm doing it wrong Stackblitz: https://stackblitz.com/edit/rzkceh-eodur9?file=demo.ts%3AL63,index.ts
import { Infer, scope } from "arktype"

export declare class TimeStub {
readonly isoString: string
/**
* @remarks constructor is private to enforce using factory functions
*/
private constructor()
/**
* Creates a new {@link TimeStub} from an ISO date string
* @param isoString - An ISO date string.
* @returns A new {@link TimeStub}
* @throws TypeError if a string is not provided, or RangeError if item
* is not a valid date
*/
static from(isoString: string): TimeStub
/**
* Creates a new {@link TimeStub} from a Javascript `Date`
* @param date - A Javascript `Date`
* @returns A new {@link TimeStub}
*/
static fromDate(date: Date): TimeStub
/**
* Get a copy of the `TimeStub` converted to a Javascript `Date`. Does not
* mutate the existing `TimeStub` value.
* @returns A `Date`
*/
toDate(): Date
/**
* Override default string conversion
* @returns the string representation of a `TimeStub`
*/
toString(): string
}

export const types = scope({
timeStub: ["instanceof", TimeStub] as Infer<TimeStub>,
account: "clientDocument&accountData",
clientDocument: {
"id?": "string",
"coll?": "string",
"ts?": ["instanceof", TimeStub] as Infer<TimeStub>,
"ttl?": ["instanceof", TimeStub] as Infer<TimeStub>
},
accountData: {
user: "user|timeStub",
provider: "provider",
providerUserId: "string"
},
user: {
name: "string",
"accounts?": "account[]"
},
provider: "'GitHub'|'Google'"
}).compile()

type Account = typeof types.account.infer
import { Infer, scope } from "arktype"

export declare class TimeStub {
readonly isoString: string
/**
* @remarks constructor is private to enforce using factory functions
*/
private constructor()
/**
* Creates a new {@link TimeStub} from an ISO date string
* @param isoString - An ISO date string.
* @returns A new {@link TimeStub}
* @throws TypeError if a string is not provided, or RangeError if item
* is not a valid date
*/
static from(isoString: string): TimeStub
/**
* Creates a new {@link TimeStub} from a Javascript `Date`
* @param date - A Javascript `Date`
* @returns A new {@link TimeStub}
*/
static fromDate(date: Date): TimeStub
/**
* Get a copy of the `TimeStub` converted to a Javascript `Date`. Does not
* mutate the existing `TimeStub` value.
* @returns A `Date`
*/
toDate(): Date
/**
* Override default string conversion
* @returns the string representation of a `TimeStub`
*/
toString(): string
}

export const types = scope({
timeStub: ["instanceof", TimeStub] as Infer<TimeStub>,
account: "clientDocument&accountData",
clientDocument: {
"id?": "string",
"coll?": "string",
"ts?": ["instanceof", TimeStub] as Infer<TimeStub>,
"ttl?": ["instanceof", TimeStub] as Infer<TimeStub>
},
accountData: {
user: "user|timeStub",
provider: "provider",
providerUserId: "string"
},
user: {
name: "string",
"accounts?": "account[]"
},
provider: "'GitHub'|'Google'"
}).compile()

type Account = typeof types.account.infer
9 Replies
Micha
MichaOP•12mo ago
It seems the intersection account: "clientDocument&accountData" is responsible for the fact, that the type popup was not appearing. I moved account from 2nd position after clientDocument and accountData and the type popup reappeared. But .infer() returns still any 🤔 // Not showing types popup
export const finalTypes = scope({
timeStub: ["instanceof", TimeStub] as Infer<TimeStub>,
account: "clientDocument&accountData",
clientDocument: {
"id?": "string",
"coll?": "string",
"ts?": ["instanceof", TimeStub] as Infer<TimeStub>,
"ttl?": ["instanceof", TimeStub] as Infer<TimeStub>
},
accountData: {
user: "user|timeStub",
provider: "provider",
providerUserId: "string"
},
user: {
name: "string",
"accounts?": "account[]"
},
provider: "'GitHub'|'Google'"
}).compile()
export const finalTypes = scope({
timeStub: ["instanceof", TimeStub] as Infer<TimeStub>,
account: "clientDocument&accountData",
clientDocument: {
"id?": "string",
"coll?": "string",
"ts?": ["instanceof", TimeStub] as Infer<TimeStub>,
"ttl?": ["instanceof", TimeStub] as Infer<TimeStub>
},
accountData: {
user: "user|timeStub",
provider: "provider",
providerUserId: "string"
},
user: {
name: "string",
"accounts?": "account[]"
},
provider: "'GitHub'|'Google'"
}).compile()
// Showing types popup
export const finalTypes = scope({
timeStub: ["instanceof", TimeStub] as Infer<TimeStub>,
clientDocument: {
"id?": "string",
"coll?": "string",
"ts?": ["instanceof", TimeStub] as Infer<TimeStub>,
"ttl?": ["instanceof", TimeStub] as Infer<TimeStub>
},
accountData: {
user: "user|timeStub",
provider: "provider",
providerUserId: "string"
},
account: "clientDocument&accountData",
user: {
name: "string",
"accounts?": "account[]"
},
provider: "'GitHub'|'Google'"
}).compile()
export const finalTypes = scope({
timeStub: ["instanceof", TimeStub] as Infer<TimeStub>,
clientDocument: {
"id?": "string",
"coll?": "string",
"ts?": ["instanceof", TimeStub] as Infer<TimeStub>,
"ttl?": ["instanceof", TimeStub] as Infer<TimeStub>
},
accountData: {
user: "user|timeStub",
provider: "provider",
providerUserId: "string"
},
account: "clientDocument&accountData",
user: {
name: "string",
"accounts?": "account[]"
},
provider: "'GitHub'|'Google'"
}).compile()
No description
Micha
MichaOP•12mo ago
Hmm, that way .infer resolves the type, but why? 🤔
export const baseTypes = scope({
timeStub: ["instanceof", TimeStub] as Infer<TimeStub>,
clientDocument: {
"id?": "string",
"coll?": "string",
"ts?": ["instanceof", TimeStub] as Infer<TimeStub>,
"ttl?": ["instanceof", TimeStub] as Infer<TimeStub>
},
accountData: {
user: "user|timeStub",
provider: "provider",
providerUserId: "string"
},
provider: "'GitHub'|'Google'"
}).compile()

export const finalTypes = scope({
account: [baseTypes.clientDocument, "&", baseTypes.accountData],
user: {
name: "string",
"accounts?": "account[]"
},
}).compile()

type Account2 = typeof finalTypes.account.infer
export const baseTypes = scope({
timeStub: ["instanceof", TimeStub] as Infer<TimeStub>,
clientDocument: {
"id?": "string",
"coll?": "string",
"ts?": ["instanceof", TimeStub] as Infer<TimeStub>,
"ttl?": ["instanceof", TimeStub] as Infer<TimeStub>
},
accountData: {
user: "user|timeStub",
provider: "provider",
providerUserId: "string"
},
provider: "'GitHub'|'Google'"
}).compile()

export const finalTypes = scope({
account: [baseTypes.clientDocument, "&", baseTypes.accountData],
user: {
name: "string",
"accounts?": "account[]"
},
}).compile()

type Account2 = typeof finalTypes.account.infer
It seems, I can narrow it down to these two problems: 1. It seems the property order inside scope() matters. If I define a property after it's being used, no popup appears for baseTypes/ clientTypes. But then how do I define types that reference each other? 2. How can a union be created where one of both types comes from another scope()?
const baseTypes = scope({
timeStub: ["instanceof", TimeStub] as Infer<TimeStub>,
clientDocument: {
"id?": "string",
"coll?": "string",
"ts?": "timeStub",
"ttl?": "timeStub"
},
provider: "'GitHub'|'Google'"
}).compile()

export const clientTypes = scope({
account: [
baseTypes.clientDocument,
"&",
{
user: "user|timeStub", // not working because timeStub is define in baseTypes, but at least the clientTypes will be resolved although `user` is from type `never`
// user: ["user", "|", baseTypes.timeStub], // if I do this, clientTypes popup disappears and it seems the whole clientTypes types will be not more resolved
provider: baseTypes.provider,
providerUserId: "string"
}
],
user: {
name: "string",
"accounts?": "account[]"
}
}).compile()
const baseTypes = scope({
timeStub: ["instanceof", TimeStub] as Infer<TimeStub>,
clientDocument: {
"id?": "string",
"coll?": "string",
"ts?": "timeStub",
"ttl?": "timeStub"
},
provider: "'GitHub'|'Google'"
}).compile()

export const clientTypes = scope({
account: [
baseTypes.clientDocument,
"&",
{
user: "user|timeStub", // not working because timeStub is define in baseTypes, but at least the clientTypes will be resolved although `user` is from type `never`
// user: ["user", "|", baseTypes.timeStub], // if I do this, clientTypes popup disappears and it seems the whole clientTypes types will be not more resolved
provider: baseTypes.provider,
providerUserId: "string"
}
],
user: {
name: "string",
"accounts?": "account[]"
}
}).compile()
Unknown User
Unknown User•12mo ago
Message Not Public
Sign In & Join Server To View
Micha
MichaOP•12mo ago
Arktype 1.0.29 Can't see which TS version stackblitz is using (I've forked David's version) https://stackblitz.com/edit/rzkceh-eodur9?file=demo.ts%3AL63,index.ts
StackBlitz
Fayxym (forked) - StackBlitz
ArkType demo demo
ssalbdivad
ssalbdivad•12mo ago
Hey sorry for the delayed response, I didn't realize until just now that my notifications for questions had been turned off. I'm taking a look at some of these I missed now and should be quicker to respond in the future! This is so weird I've never seen anything like this (where the ordering of object keys in TS affects inference, even outside ArkType)
ssalbdivad
ssalbdivad•12mo ago
That might just be some artifact of StackBlitz though as locally I'm at least able to see the root types (though that intersection should be reduced)
No description
ssalbdivad
ssalbdivad•12mo ago
I've seen some problems with inference for cyclic intersections/unions in the past but I thought I had resolved those on alpha, I'm investigating this now as clearly alpha is not a fan of these types in general. Well it works fine on beta which is a relief It's also very fast on beta where something going on in alpha is really slowing down the language server for that scope
ssalbdivad
ssalbdivad•12mo ago
No description
ssalbdivad
ssalbdivad•12mo ago
Honestly I'd love to figure out where in alpha things are going awry but given I'll be releasing a 2.0 prerelease that is mostly working in the next few days it might be better to just add your example as a test case and ensure it continues to work well there

Did you find this page helpful?