在下游接口上保留基于字段内容的联合类型区分

时间:2019-03-07 11:50:16

标签: typescript

这是场景。

我有一个接口,表示通用var aud = document.getElementById("myAudio"); aud.onplay = function() { alert("The audio has started to play"); }; 的元数据(假设我们正在谈论Angular Material的Field实例),它由名称和类型组成,该类型和类型是常见输入类型的列表

MatFormField

某些export type FieldType = 'string' | 'number' | 'date' | 'datetime' | 'select'; export interface FieldMetadata { readonly type: FieldType; name: string; } 仅适合那些元数据,而其他Field则需要附加信息,因此需要接口扩展。 然后,我定义了一个联合类型,因此我可以根据“类型”字段获得正确的类型提示。

// Specific interface for select field
export interface SelectFieldInterface extends FieldMetadata {
  readonly type: 'select';
  options: string[];
}

// All other fields which doesn't need additional info
export interface GenericFieldInterface extends FieldMetadata {
  readonly type: Exclude<FieldType, 'select'>;
}

export type Field = GenericFieldInterface | SelectFieldInterface;

// Understands it's a GenericFieldInterface
const genericField1: Field = {
  type: 'number',
  name: ''
};

const genericField2: Field = {
  type: 'number',
  name: '',
  option: [] // <---- Type Error, unexpected 'options'
};

// Understands it's a SelectFieldInterface
const selectField1: Field = {
  type: 'select',
  name: '',
  options: []
};

const selectField2: Field = {
  type: 'select',
  name: '',
  // <----- Type Error, expected 'options'
};

这里一切都按预期进行(但是,如果您知道更好的管理方式,请分享)。

当我想以这种抽象为基础时,就会出现问题。

让我们说,应该在这种联合类型的顶部构建一个新模型,以保持区别。 我希望可以得到类似的结果。

export interface Model extends Field {
  additionalProperty: string;
}
在TypeScript中

BUT 这是不可行的,您不能扩展联合类型。 我通过定义一个接受所有Field类型的所有属性的中间“超类型”来部分解决了这个问题。

我必须在特定实现上省略类型属性,否则会因接受的类型值冲突而出现问题。

export type ExtendedField = FieldMetadata &
  Partial<Omit<GenericFieldInterface & SelectFieldInterface, 'type'>>;

但是这样我就失去了两件事。

首先是区分各种实现方式。

const genericField: Model = {
  type: 'number',
  name: '',
  additionalProperty: '',
  option: [] // <---- Doesn't fire Type Error for 'option' property, it should
};

const selectField: Model = {
  type: 'select',
  name: '',
  additionalProperty: '',
  // <---- Doesn't fire Type Error for missing 'option' property, it should
};

第二个是FieldModel签名之间的兼容性:我不能在签名要求Model的地方使用Field。 当然,知道它们是兼容的,但是编译器不兼容,因此我不得不将Model强制转换为Field

function someFn(field: Field) {}

const selectField: Model = {
  type: 'select',
  name: '',
  additionalProperty: '',
  option: []
};

someFn(selectField) <------ Type Error
someFn(selectField as Field) <------ Works with casting

关于如何更好地表达这些矛盾的任何想法?我试图研究条件类型,但是我不明白它们是否对我有用...

1 个答案:

答案 0 :(得分:1)

最简单的解决方案是将类型别名与交集一起使用,以添加所需的额外属性。只要这些类型仅用于类型检查对象文字,而不必由类实现,则它们应该都能正常工作:

 CREATE PROCEDURE [dbo].[proc_SaveAccessRequest] 
(
  @TmpAR TmpAccessRequest READONLY,
  @IsUAMSRequest bit,
  @RequestID int OUTPUT
) 
AS  
BEGIN

     Insert into tblRequests
    (
       RequesterID
      ,RequestType
      ,NextApprover
      ,RequestStatus
      ,Delegation
      ,CreatedOn
      ,CreatedBy
      ,[Description]
      ,IsSepecialRequest
      ,DelegationDetailID
      ,IsActive
      ,IsDeleted
      ,ModifiedOn
    )
    SELECT
       RequesterID
      ,RequestType
      ,NextApprover
      ,RequestStatus
      ,Delegation
      ,CreatedOn
      ,CreatedBy
      ,Description
      ,IsSepecialRequest
      ,DelegationDetailID
      ,IsActive
      ,IsDeleted
      ,ModifiedOn
     FROM @TmpAR  
     WHERE NOT EXISTS ( SELECT 1 
                        FROM tblRequests i
                        INNER JOIN @TmpAR o  
                            ON i.RequesterID = o.RequesterID
                         AND i.RequestType = o.RequestType 
                         AND i.NextApprover = o.NextApprover)

     SELECT @RequestID = SCOPE_IDENTITY()

     SELECT @RequestID
END