棱形切片的打字稿类型

问题描述

我正在使用Prismic,Gatsby和Typescript建立一个网站。切片功能是Prismic吸引人的一部分,它使您可以创建动态内容部分,以使内容编辑者更易于使用。我有一个名为SliceZone的组件,它可以映射页面上的所有切片:

SliceZone.tsx

import React from 'react';

import { ImageCta } from 'components/slices/call-to-action/image/ImageCta';
import { Columns } from 'components/slices/columns/Columns';
import { ContentBlock } from 'components/slices/content-block/ContentBlock';
import { Embed } from 'components/slices/embed/Embed';
import { TextHero } from 'components/slices/hero/text/TextHero';
import { Slider } from 'components/slices/slider/Slider';
import { Video } from 'components/slices/video/Video';

interface PageSlicesProps {
  slices: any;
}

const PageSlices = ({ slices }: PageSlicesProps) => {
  const sliceComponents = {
    hero: TextHero,slider: Slider,content_block: ContentBlock,video: Video,columns: Columns,embed: Embed,image_call_to_action: ImageCta,};

  return slices.map((slice: any,index: number) => {
    const SliceComponent = sliceComponents[slice.type];
    
    if (SliceComponent) {
      return <SliceComponent slice={slice} key={`slice-${slice.type}-${index}`} />;
    }
  }); 
};

interface SliceZoneProps {
  bodyContent: any;
}

export const SliceZone = ({ bodyContent }: SliceZoneProps) => <PageSlices slices={bodyContent.body} />;

,我需要正确键入所有内容。但是,我在SliceComponent上收到此错误,特别是sliceComponents[slice.type]

Element implicitly has an 'any' type because expression of type 'any' can't be used to index type '{ hero: ({ slice }: any) => Element; slider: ({ slice }: any) => Element; content_block: ({ slice }: any) => Element; video: ({ slice }: any) => Element; columns: ({ slice }: any) => Element; embed: ({ slice }: { ...; }) => Element; image_call_to_action: ({ slice }: any) => Element; }'.

如何创建一个interface来正确键入此内容?如果我的问题有些混乱,我感到很抱歉,我是React和Typescript的新手,所以到目前为止,这种语言还是有点不适。

解决方法

问题在于您对@page "/Students2"; @using ParentChild.Models; @inject IJSRuntime JS; // Used for Confirmation Dialog & Alert Box @inject NavigationManager NavigationManager // Used to Refresh Entire Page <h3>Students 2</h3> @if (byStudents == null) { <p><em>Loading...</em></p> } else if (!byStudents.Any()) { <p><em>No students in database. Please add some.</em></p> } else { <table class="table"> <thead> <tr> <th>First</th> <th>Last</th> <th>Street</th> <th>City</th> <th>State</th> <th>Zip</th> <th>Cell</th> <th>Home</th> <th>Email</th> <th>Note</th> <th></th> </tr> </thead> <tbody> @foreach (var byStudent in byStudents) { <tr> <td>@byStudent.SFirst</td> <td>@byStudent.SLast</td> <td>@byStudent.SStreet</td> <td>@byStudent.SCity</td> <td>@byStudent.SState</td> <td>@byStudent.SZip</td> <td>@byStudent.SCell</td> <td>@byStudent.SHome</td> <td>@byStudent.SEmail</td> <td>@byStudent.SNote</td> <td> <input type="button" class="btn btn-primary" @onclick="@(() => OpenEditStudent(@byStudent.SId))" value="Edit" /> <nbsp></nbsp> <input type="button" class="btn btn-danger" @onclick="@(async () => await DeleteStudentAction(@byStudent.SId))" value="Delete" /> </td> </tr> } </tbody> </table> } <button class="btn btn-primary" @onclick="() => OpenAddStudent()">Add New Student</button> @if (showBackdrop) { <div class="modal-backdrop fade show"></div> } <!-- Define Modal Pop-UP in this Page --> <div class="modal @modalClass" tabindex="-1" role="dialog" style="display:@modalDisplay; overflow-y: auto;"> <div class="modal-dialog modal-lg" role="document"> <div class="modal-content"> <div class="modal-header"> <h5 class="modal-title"> <!-- Pop-Up Title Section --> @{ var txt = ""; if ((SId == null) | (SId == "")) { txt = "Add Student"; @txt; } else { txt = "Edit Student"; @txt; } } </h5> <!-- Pop-Up Close Button at Top --> <button type="button" class="close" data-dismiss="modal" aria-label="Close" @onclick="Close"> <span aria-hidden="true">&times;</span> </button> </div> <div class="modal-body"> <!-- Pop-Up Body Section --> <div class="form-group row"> <input id="SId" @bind="SId" type="hidden" class="col-sm-4 form-control" /> </div> <div class="form-group row"> <label for="@SFirst" class="col-sm-8 col-form-label">First Name:</label> <input id="SFirst" @bind="SFirst" class="col-sm-4 form-control" /> </div> <div class="form-group row"> <label for="@SLast" class="col-sm-8 col-form-label">Last Name:</label> <input id="SLast" @bind="SLast" class="col-sm-4 form-control" /> </div> <div class="form-group row"> <label for="@SStreet" class="col-sm-8 col-form-label">Street:</label> <input id="@SStreet" @bind="SStreet" class="col-sm-4 form-control" /> </div> <div class="form-group row"> <label for="@SCity" class="col-sm-8 col-form-label">City:</label> <input id="@SCity" @bind="SCity" class="col-sm-4 form-control" /> </div> <div class="form-group row"> <label for="@SState" class="col-sm-8 col-form-label">State:</label> <input id="@SState" @bind="SState" class="col-sm-4 form-control" /> </div> <div class="form-group row"> <label for="@SZip" class="col-sm-8 col-form-label">Zip:</label> <input id="@SZip" @bind="SZip" class="col-sm-4 form-control" /> </div> <div class="form-group row"> <label for="@SCell" class="col-sm-8 col-form-label">Cell #:</label> <input id="@SCell" @bind="SCell" class="col-sm-4 form-control" /> </div> <div class="form-group row"> <label for="@SHome" class="col-sm-8 col-form-label">Home #:</label> <input id="@SHome" @bind="SHome" class="col-sm-4 form-control" /> </div> <div class="form-group row"> <label for="@SEmail" class="col-sm-8 col-form-label">Email:</label> <input id="@SEmail" @bind="SEmail" class="col-sm-4 form-control" /> </div> <div class="form-group row"> <label for="@SNote" class="col-sm-8 col-form-label">Note:</label> <input id="@SNote" @bind="SNote" class="col-sm-4 form-control" /> </div> </div> <div class="modal-footer"> <!-- Pop-Up Body Section --> @{ if ((SId == null) | (SId == "")) { <button type="button" class="btn btn-primary" @onclick="async () => await AddStudentAction()">Add</button> } else { <button type="button" class="btn btn-primary" @onclick="async () => await UpdateStudentAction(this.SId)">Update</button> } } <button type="button" class="btn btn-secondary" data-dismiss="modal" @onclick="() => this.Close()">Close</button> </div> </div> </div> </div> @code { //------------------------------------------------------------------------- // Modal Control Variables and Code //------------------------------------------------------------------------- private string modalDisplay = "none;"; private string modalClass = ""; private bool showBackdrop = false; //--- Open Modal... public void Open() { modalDisplay = "block;"; modalClass = "show"; showBackdrop = true; } //--- Close Modal... public void Close() { modalDisplay = "none"; modalClass = ""; showBackdrop = false; } //------------------------------------------------------------------------- // Student List from Database //------------------------------------------------------------------------- DB_136837_byogaContext db = new DB_136837_byogaContext(); private List<ByStudents> byStudents; //--- Build Student List on Page Initialization... protected override void OnInitialized() { byStudents = db.ByStudents.ToList(); } //------------------------------------------------------------------------- // Modal Parameters //------------------------------------------------------------------------- private Modal modal { get; set; } [Parameter] public string SId { get; set; } = ""; [Parameter] public string SFirst { get; set; } = ""; [Parameter] public string SLast { get; set; } = ""; [Parameter] public string SStreet { get; set; } = ""; [Parameter] public string SCity { get; set; } = ""; [Parameter] public string SState { get; set; } = ""; [Parameter] public string SZip { get; set; } = ""; [Parameter] public string SCell { get; set; } = ""; [Parameter] public string SHome { get; set; } = ""; [Parameter] public string SEmail { get; set; } = ""; [Parameter] public string SNote { get; set; } = ""; //------------------------------------------------------------------------- // Open Modal to Add Student //------------------------------------------------------------------------- protected void OpenAddStudent() { //-- Clear Any Info Set in Previous Edit Requests... SId = ""; SFirst = ""; SLast = ""; SStreet = ""; SCity = ""; SState = ""; SZip = ""; SCell = ""; SHome = ""; SEmail = ""; SNote = ""; //--- Open Modal Popup... this.Open(); } //------------------------------------------------------------------------- // Open Modal to Edit Student //------------------------------------------------------------------------- protected void OpenEditStudent(int myID) { //--- Build & Run SQL Select Statement... using (var myContext = new DB_136837_byogaContext()) { //--- Grab Info for Selected Student... var myQuery = (from s in myContext.ByStudents where s.SId == myID select s).Single(); //--- Set Fields on Popup Form... this.SId = myQuery.SId.ToString(); this.SFirst = myQuery.SFirst; this.SLast = myQuery.SLast; this.SStreet = myQuery.SStreet; this.SCity = myQuery.SCity; this.SState = myQuery.SState; this.SZip = myQuery.SZip; this.SCell = myQuery.SCell; this.SHome = myQuery.SHome; this.SEmail = myQuery.SEmail; this.SNote = myQuery.SNote; } //--- Open Modal Form... this.Open(); } //------------------------------------------------------------------------- // Delete Selected Student from Database with Confirmation Dialog //------------------------------------------------------------------------- protected async Task DeleteStudentAction(int myID) { bool isConfirmed = await JS.InvokeAsync<bool>("confirm",$"Do you sure you want to permanently delete whis record?"); if (isConfirmed) { //--- Build & Run SQL Delete Statement... using (var myContext = new DB_136837_byogaContext()) { var myQuery = (from d in myContext.ByStudents where d.SId == myID select d).Single(); myContext.ByStudents.Remove(myQuery); myContext.SaveChanges(); } //--- Delete Complete Dialog... await JS.InvokeVoidAsync("alert","Record Deleted"); //--- Refresh Page... await InvokeAsync(() => { StateHasChanged(); this.OnInitialized(); }); } } //------------------------------------------------------------------------- // Add Student to Database //------------------------------------------------------------------------- protected async Task AddStudentAction() { //--- Define the New Student... var newStudent = new ByStudents(); newStudent.SFirst = SFirst; newStudent.SLast = SLast; newStudent.SStreet = SStreet; newStudent.SCity = SCity; newStudent.SState = SState; newStudent.SZip = SZip; newStudent.SCell = SCell; newStudent.SHome = SHome; newStudent.SEmail = SEmail; newStudent.SNote = SNote; //--- Add New Student to Database... using (var myContext = new DB_136837_byogaContext()) { myContext.ByStudents.Add(newStudent); myContext.SaveChanges(); } //--- Close the Popup... this.Close(); //--- Refresh Page... await InvokeAsync(() => { StateHasChanged(); this.OnInitialized(); }); } //------------------------------------------------------------------------- // Update Student in Database //------------------------------------------------------------------------- protected async Task UpdateStudentAction(string myID) { //--- Build & Run SQL Select Statement... using (var myContext = new DB_136837_byogaContext()) { //--- Update Info for Selected Student... var myQuery = (from d in myContext.ByStudents where d.SId == Convert.ToInt32(myID) select d).Single(); myQuery.SFirst = this.SFirst; myQuery.SLast = this.SLast; myQuery.SStreet = this.SStreet; myQuery.SCity = this.SCity; myQuery.SState = this.SState; myQuery.SZip = this.SZip; myQuery.SCell = this.SCell; myQuery.SHome = this.SHome; myQuery.SEmail = this.SEmail; myQuery.SNote = this.SNote; myContext.ByStudents.Update(myQuery); myContext.SaveChanges(); } //--- Close the Popup... this.Close(); //--- Refresh Page... await InvokeAsync(() => { StateHasChanged(); this.OnInitialized(); }); //--- Refresh Page by Reloading...SHOULD NOT HAVE TO DO THIS! //NavigationManager.NavigateTo("Refresh/Students2"); } } 的映射。它缺少签名,因此返回类型隐式为const sliceComponents =。 如错误所示。

您可以通过说出自己有一个React类组件/功能组件的列表来使之明确吗?

any

const sliceComponentsMap: { [key: string]: typeof Component | FunctionComponent; } union 运算符。这说明可以是这些类型中的任何一种。


但是我认为创建一个通用的slice类来扩展React Component类会更好。

|

并让您的slice组件扩展它。

export interface sliceCommonProps {
  id: string;
}

// Interface for base slice props
export interface sliceProps<P> {
  sliceType: string;
  sliceData: P & sliceCommonProps; // This "&" joins the 'Generic' param P with the common props interface
}

export interface sliceState {}

// Base class with two generic params (P,S) for passing specific component props and state interfaces later
export class SliceComponent<
  P = { [key: string]: any },S = { [key: string]: any }
> extends Component<sliceProps<P>,sliceState> {}

然后,当您将它们映射到其组件时,可以说它们的类型为SliceComponent。并且编译器现在知道该组件应该具有正确的道具。

export interface ImageCtaProps {
  imageUrl: string;
}

export class ImageCta extends SliceComponent<ImageCtaProps> {
  render() {
    const { id,imageUrl } = this.props.sliceData;
    return <div>{imageUrl}</div>;
  }
}

Check this stack