【问题标题】:Typescript: ensure object literal extends interface but return real object type打字稿:确保对象文字扩展接口但返回真实对象类型
【发布时间】:2021-03-14 20:36:46
【问题描述】:

我正在尝试在 typescript 中创建一个函数,该函数返回一个必须扩展给定接口的对象,但我希望该函数返回所创建对象的真实类型。 原因是函数中的对象将来可能会发生变化,我想确保它始终具有界面所需的最少道具。

示例:

interface MustExtend {
  a: string;
}

function myFunc() {
  // I want to enforce res to extend type MustExtend
  // right now it can be of type {something: 3} and compiler will allow it
  const res = {a: 'hello', b: 2} 
  return res;
}

const c = myFunc(); // c should be of type {a: string, b: number}, or the more concrete type generated by method

编辑:

我会尽量澄清我的问题。我希望从返回的对象中推断出函数的结果类型,而不指定 res 的类型,因为它是由许多计算生成的:

interface MustExtend {
  a: string;
}

function myFunc() {
  // i want to enforce res to extend type MustExtend
  // right now it can be of type {something: 3} (no 'a' at all)
  // and i want to make sure it exists
  const res = {
    a: 'hello', 
    b: 2, 
    // a million more properties here that can change over time
  } 
  return res;
}

const c = myFunc(); // c should be of type {a: string, b: number, ...other props}

【问题讨论】:

  • 你能试着解释一下吗?我以为我有你的答案,但随着我阅读你的问题,我越来越困惑。
  • 本质上,我希望我的方法为在方法中创建的对象返回一个尽可能具体的值(不仅仅是MustExtend 接口)。在我的示例中,我可以这样做:const res = {somethingElse: 5} 并且编译器不会大喊大叫,因为我没有强制执行任何操作。我想要两全其美:)
  • function myFunc(): MustExtend {
  • @nubinub 这不起作用,因为c 的类型为MustExtend 而不是{a: string, b: number} 类型,编译器将无法识别返回值中存在的属性b
  • interface C extends MustExtend { b: number; } 然后function myFunc(): C {

标签: typescript


【解决方案1】:

你可以使用一个技巧。 强制 Typescript 将 res 转换为 MustExtend,但随后您将其转换回以保留原始类型:

interface MustExtend {
  a: string;
}

function myFunc() {
  const res  = {
    a: 'hello', // will cause TS error if you remove this line
    b: 2,
    1: '1',
  } 

  return res as MustExtend as typeof res; // convert and convert back
}

const c = myFunc(); // will have full type

【讨论】:

  • 这也是一个很酷的技巧,如果它不兼容,编译器会在第一次转换时失败。我不太喜欢的是,如果我做一些更复杂的逻辑,我可能会错过这些转换中的属性。
  • 你是什么意思“错过属性”?
  • 我通常会尽量避免强制转换,因为它往往会导致属性不匹配的问题,尽管在这种情况下不会真正发生。
【解决方案2】:

经过一番修改,我找到了方法:

interface MustExtend {
  a: string;
}

function myFunc(): typeof res extends MustExtend ? typeof res : never {
  // i want to enforce res to extend type MustExtend
  // right now it can be of type {something: 3} (no 'a' at all)
  // and i want to make sure it exists
  const res = {
    a: 'hello', 
    b: 2, 
    // a million more properties here that can change over time
  } 
  return res;
}

const c = myFunc(); // c should be of type {a: string, b: number, ...other props}

显然打字稿可以在编译时获取方法中对象的类型并在方法类型中使用它。以这种方式使用它我不必提前指定res 的类型,并确保它具有接口中的所有属性

【讨论】:

    【解决方案3】:

    函数返回类型定义可以是类类型,也可以是接口。如果您要定义这样的函数:

    function myFunc(): MustExtend {}
    

    其中MustExtend是一个接口,它将返回符合接口MustExtend的对象。但是返回的对象仍然有它的类型,可以用instanceof检查。实际上,在打字稿中,您无法检查返回的对象是否符合您想要的接口 - instanceof 不适用于接口。所以你必须检查对象的实际类型。使用此定义,您可以返回符合MustExtend(可怕的名称,名词上的名称接口:)的任何对象,然后您可以检查(如果需要)它的确切类型。希望对您有所帮助。

    【讨论】:

      【解决方案4】:

      您的函数已经返回了可能的最通用类型。当您使用返回值时,强制此类型与接口匹配。例如,您可以进行这样的检查:

      const check: () => MustExtend = () => myFunc()
      // or
      const check1: MustExtend | undefined = undefined as ReturnType<typeof myFunc> | undefined;
      

      Playground

      据我所知,如果您只需要在类型级别强制执行测试(不生成 javascript 对象),您需要一个外部工具,例如 dtslint.

      【讨论】:

        【解决方案5】:
        interface MustExtend {
          a: string;
        }
        
        const c = {
          a: 'hello',
          b: 2,
        }
        // Ensure `c` conforms to MustExtend. (`c` will continue to have its original type)
        c as MustExtend // Works!
        
        const d = {
          x: 1,
          y: 2,
        }
        // Ensure `d` conforms to MustExtend
        d as MustExtend // Error! Property 'a' is missing
        

        playground

        【讨论】:

        • 如果 c 将是 const c = {a: 'hello', b: 2, x: 3} 然后运行 ​​const c = myFunc(); 将不会返回 c 的实际类型,但 MustExtend 的类型不是我想要的。检查我的答案,你会得到c 的实际类型
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2019-09-01
        • 1970-01-01
        • 1970-01-01
        • 2023-03-29
        • 2019-01-16
        • 2018-10-10
        • 2022-01-11
        相关资源
        最近更新 更多