【问题标题】:How in Typescript transform interface members to Tagged Template Literal (TTL)?Typescript 如何将接口成员转换为标记模板文字(TTL)?
【发布时间】:2022-02-18 23:19:31
【问题描述】:

我的主要想法是使用 Typescript 生态系统作为“编辑器”来创建基于“域”示例的降价规范 - 最安全的类型方式,自动完成和编译错误的“通知” ? ???

到目前为止,我的这段代码存在问题:

/* TYPES.ts */

// a SPEC-ification TTL (implementation is omited, it is only prototype for now)
declare function SPEC (docs: TemplateStringsArray, ...domainExamples: Glossary[]): ExFnc
type ExFnc = (optionalOverloadingDomainExample?: Object) => string

// ... rest of types are omited here - pls see them by link to TS Playground


/* DOMAIN_GLOSSARY.ts */

// At   = Attribute
// roAt = read-only Attribute
// Ac   = Action
type Glossary
//      Label              Example 1                       Example N
= At    <`Url`,           `system.org/login`>
| At    <`User Name`,     `The.Valid@g.mail`             | `An.Invalid@g.mail`>
| At    <`Password`,      `existing`                     | `invalid`>
| Ac    <`Login`>
| roAt  <`Error message`,                                  `Invalid User Name or Password`>


/* SPECIFICATION_BY_EXAMPLE.ts */

const S1_Alt2 = SPEC
`
## Alternative scenario: Invalid login

- Given
  - ${`Url: system.org/login`}

- When
  - ${`User Name: The.Valid@g.mail`}   
  - ${`Password: invalid`}
  - ${`Login`}

- Then
  - ${`Error message? Invalid User Name or Password`}
`

/* TEST_CASES.ts */

// Case here is OK Scenario call - because there STILL is "original" domain Example
S1_Alt2 () 

// Case here is OK Scenario call - because "correctly" overloading original domain Example
S1_Alt2 ({ 

  'User Name':  `An.Invalid@gmail.com`,
  'Password':   `existing`
})

// Case here is KO Scenario call - because "INcorrectly" overloading original domain Example
// and I expected stated "approximate" compile errors
S1_Alt2 ({ 
           

  'Useeer Name':  `An.Invalid@gmail.com`, 
   // Object literal may only specify known properties, and ''Useeeer Name'' does not exist in type '...'

  'Password':     `exiiisting`,            
   // Type '"exiiisting"' is not assignable to type '"existing"'.
   // The expected type comes from property 'Password' which is declared here on type '...'

   'Home page title': 
   // Object literal may only specify known properties, and ''Home page title'' does not exist in type '...' 
   // (pls note: this is because `Home page title...` was NOT interpollated in const S1_Alt1 = SPEC<uiLogin>`...` )
})

TS playground with complete code and types so far

1.问题 - 输入ExFnc:

  • 如何声明其单个参数类型Object - 以便对象属性:
    • 只能来自词汇表属性,但不能来自操作
    • 只能来自插花
    • 必须是可选的
    • 以及所有深层次
  • 我认为 仅此一个问题 - 就是大挑战!!!

2。问题 - 简洁的“Glossarifying”

  • 我正在寻找更简洁的语法来“Glossarify” - 基于接口
  • 分别。如何将接口转换为“类型安全”标记的模板文字?
    • 接口的所有函数成员都应转换为 TTL 中的“动作”
    • 和所有非函数成员作为“属性”

3.问题 - 键入 S1_Alt2 的安全调用({ 带自动完成 })

  • 请在最后一次调用 S1_Alt2 时查看对象字面量 - '用户名' 属性或 'exiiisting' 值 - 我预计会出现编译错误
  • 或者如果Login 在这里声明 - 我预计也会出现编译错误
    • 因为操作不能“重载”(只有属性可以) - 否则我们有“ANOTHER/STRANGE”“场景”(想象Cancel 场景调用中的操作!!!)
  • 或者如果Home page title... 在这里声明 - 我预计也会出现编译错误
    • 因为属性 Home page title... 未在原始 const S1_Alt2 = SPEC`...` 中进行插值处理
  • 并且所有属性都应该是可选的,但在此处具有自动完成功能
    • 因为它们已经在 const S1_Alt2 中声明了

有什么建议吗?

【问题讨论】:

  • 请看example。更多的是解决第一个问题,至于其他问题,很难理解您的期望。您能否提供更多带有 cmets 的测试用例。 FO 实例:expected error because ...should be ok because .... ?就个人而言,我认为您应该将您的问题分成几个问题,但这只是我的想法。不管怎样,你的问题似乎很有趣
  • 你想如何将接口转换为标签联合?界面的哪一部分应该分开?你能提供一个预期的例子吗? this 是您正在寻找的东西吗?
  • 我不知道你对标记的动作和属性联合有什么要求,但你可以试试this
  • 深嵌套this
  • @captain-yossarian 太棒了!!!非常感谢 - 2. 问题完全解决,也是 3. 问题的一部分。仅保留用于场景规范调用的可选性和排除操作。到目前为止,我有这个declare function SPEC&lt;UI&gt; (docs: TemplateStringsArray, ...domainExamples: Glossarify&lt;UI&gt;[]): (optionalOverloadingDomainExample?: Partial&lt;UI&gt; ) =&gt; string。请问如何使Partial&lt;UI&gt;仅排除Actions?(即仅排除接口函数成员)

标签: typescript types typescript-generics template-literals definitelytyped


【解决方案1】:

对于这个答案 - 此处的所有积分都归于 @captain-yossarian

(注意:仍然有很大的挑战 1. PROBLEM - 但其余部分已经在正确的轨道上 :)

H I N T:对于全局,请开始阅读来自 /* I. (域)GLOSSARY.ts */ part ?

/* 0. TYPES.ts */

// SPEC-ification is Tagged Template Literal based on <UserInterface>, interpolated with Domain Glossary terms (aka Domain Attributes & Actions)  
// (TTL implementation is not important here)
declare function SPEC<UI> (docs: TemplateStringsArray, ...domainExamples: Glossarify<UI>[]): 
  (optionalOverloadingDomainExample?: Partial<UI> ) => string

// GLossarify is type to transform an "User" Interface to Domain Glossary Attributes & Actions
type Glossarify<Obj> = {
  [Key in keyof Obj]:
    
  Obj[Key] extends Action ? ActionSyntax<Key> 
  // lets define interface FUNCTION member AS an ACTION in Domain resp. button/link element in UI - like:
  // `Login`                         --> here with CLICK operator allowed only
  
  : Obj[Key] extends Attribute ? AttributePlaceholderSyntax<Key> | AttributeSetSyntax<Obj, Key> | AttributeAssertSyntax<Obj, Key>
  // lets define REST interface members AS ATTRIBUTES in Domain resp. input elements in UI - like:
  // `User Name: An.Invalid@g.mail`  --> here with SET operator syntax on Attribute / UI element
  // `User Name? The.Valid@g.mail`   --> here with ASSERT operator syntax against Attribute / UI element (to assert default "onLoad" value for example)
  // `User Name`                     --> here with PLACEholder only syntax :)
  
  : ActionSyntax<Key> | Glossarify<Obj[Key]>
  // lets define DEEP KEY also AS ACTION | deep recursion

}[keyof Obj]

// Attributes & Actions helpers
type Attribute                                          = string | boolean | number | bigint | symbol | null | undefined
type AttributePlaceholderSyntax<Key>                    = `${Key & string}${placeholder}`
type AttributeSetSyntax<Obj, Key extends keyof Obj>     = `${Key & string}${set} ${Obj[Key] & string}`
type AttributeAssertSyntax<Obj, Key extends keyof Obj>  = `${Key & string}${assert} ${Obj[Key] & string}`

type Action                                             = Function
type ActionSyntax<Key>                                  = `${Key & string}${click}`

// "operators" for Attributes & Actions
type placeholder  = ``
type set          = `:`
type assert       = `?`
type click        = `` // `` here is confusing, I know - and yes, you can imagine something like `*` instead 
                       // but `Login` syntax is shorter for me - than `Login*` ;)


                       
/* I. (domain) GLOSSARY.ts */

interface uiLogin {
// Domain Attribute/Action  Domain Example 1    | Domain Example N
        'Url':             `system.org/login`
        'User Name':       `The.Valid@g.mail`   | `An.Invalid@g.mail`                   
        'Password':        `existing`           | `invalid`                  
        'Login': Action

        'Error message':                          `Invalid User Name or Password`
        
        'Home page title': `Welcome The.Valid !`
 
        Deep: {
          Att1:            `value1`             | `value2`
          Action1: Action
        }

// and yes - all should idealy work at any interface "deep" level :)        
}

interface uiPage_N { 
  // ...
}



/* II. (living) SPECIFICATION_BY_EXAMPLES.ts */

const S1_Happy = SPEC<uiLogin>
`
## Happy scenario: Succesful login

- Given
  - ${`Url: system.org/login`}

- When
  - ${`User Name: The.Valid@g.mail`}
  - ${`Password: existing`}
  - ${`Login`}

- Then
  - ${`Home page title? Welcome The.Valid !`}
`

const S1_Alt1 = SPEC<uiLogin>
`
## Alternative scenario: Invalid login

- Given
  - ${`Url: system.org/login`}

- When
  - ${`User Name: The.Valid@g.mail`}   
  - ${`Password: invalid`}
  - ${`Login`}

- Then
  - ${`Error message? Invalid User Name or Password`}
`


/* III. (test) CASES.ts */

// Case here is OK Scenario spec call - because with "original" domain Examples
S1_Happy()

// Case here is OK Scenario spec call - because correctly "overloading" original domain Examples
S1_Alt1({

  'User Name': `An.Invalid@g.mail`,
  'Password': `existing`
})

// Case here is KO Scenario spec call - because INcorrectly overloading original domain Examples
// and I expected stated "approximate" compile errors
S1_Alt1 ({ 

  'Useeer Name':  `An.Invalid@g.mail`, 
   // Object literal may only specify known properties, and ''Useeeer Name'' does not exist in type '...'

  'Password':     `exiiisting`,            
   // Type '"exiiisting"' is not assignable to type '"existing"' | ...
   // The expected type comes from property 'Password' which is declared here on type '...'

  'Home page title': 
  // Object literal may only specify known properties, and ''Home page title'' does not exist in type '...' 
  // (pls note: this is because `Home page title...` was NOT interpollated in "original" const S1_Alt1 = SPEC<uiLogin>`...` )
})

【讨论】:

  • 能否提供您项目的链接?看起来很有趣
  • @captain-yossarian 当然可以,但到目前为止我还没有任何项目,对不起。如果我们能以这种方式使用 Typescript 生态系统,那只是一个想法。我们绝对可以,谢谢 - 想法是公开的 :)
  • 一旦你开始做某事,请ping我
  • @captain-yossarian 我想我会开始,因为这样我们就可以开始与分析师一起工作,并精确任何领域,如 Markdown 中的规范(UseCase、UserStory、Guide、Acceptance Test、.. .)。所以我们可以有“N in 1”规范。请想象一下——这个规范可以在 TTL 中执行,例如 E2E 自动化测试。如果我们可以将interfaces.ts 构建到项目中——连同分析师和测试人员的规范——它可以成为真正的活文档!
  • 当然,听起来不错!
猜你喜欢
  • 2017-04-25
  • 1970-01-01
  • 1970-01-01
  • 2018-05-05
  • 2018-10-27
  • 2018-03-19
  • 2021-01-22
  • 2021-01-17
  • 2019-08-29
相关资源
最近更新 更多