简体   繁体   中英

I can't define type to set an array of objects in a variable React.useState

I'm trying to set values that come from an.xlsx using the SheetJS library. Below I'll present the codes and error, and soon after the forms I've tried.

Data output from var dataToJson :

(6) [{…}, {…}, {…}, {…}, {…}, {…}]
0: {idSeller: 503, idSupplier: 20, financialGoal: 0, positivationGoal: 300, qtVenda: 0, …}
1: {idSeller: 3113, idSupplier: 9, financialGoal: 400000, positivationGoal: 6, qtVenda: 0, …}
2: {idSeller: 7303, idSupplier: 378, financialGoal: 390000, positivationGoal: 0, qtVenda: 0, …}
3: {idSeller: 3113, idSupplier: 964, financialGoal: 360000, positivationGoal: 0, qtVenda: 0, …}
4: {idSeller: 7116, idSupplier: 378, financialGoal: 310000, positivationGoal: 0, qtVenda: 0, …}
5: {idSeller: 7117, idSupplier: 378, financialGoal: 300000, positivationGoal: 0, qtVenda: 0, …}
length: 6
__proto__: Array(0)

Data inside objects:

5:
financialGoal: 300000
idSeller: 7117
idSupplier: 378
positivationGoal: 0
qtVenda: 0
__rowNum__: 6
__proto__: Object

Interface and code:

interface IXlsxData {
  financialGoal: number;
  idSeller: number;
  idSupplier: number;
  positivationGoal: number;
  qtVenda: number;
  // __rowNum__: number;
};
    
const Archive: React.FC = () => {
  const [files, setFiles] = useState<IXlsxData>(null);
    
  const readExcel = (e) => {
    e.preventDefault();
    
    var file = e.target.files[0];
    var reader = new FileReader();
    reader.readAsArrayBuffer(file);
    
    reader.onload = (e) => {
    /* Parse data */
    let data = e.target.result;
    let readedData = XLSX.read(data, {type: 'buffer'});
    
    /* Get first ws (worksheet) */
    const wsname = readedData.SheetNames[0];
    const ws = readedData.Sheets[wsname];

    /* Convert array to json */
    const dataToJson = XLSX.utils.sheet_to_json(ws);
    setFiles(dataToJson); // ---- ERROR ----
    // setFiles([...files, dataToJson]);
  };
}

Error:

const dataToJson: unknown[]
Argument of type 'unknown[]' is not assignable to parameter of type 'SetStateAction<IXlsxData[]>'.
  Type 'unknown[]' is not assignable to type 'IXlsxData[]'.
    Type '{}' is missing the following properties from type 'IXlsxData': financialGoal, idSeller, idSupplier, positivationGoal, qtVendats(2345)

I've already tried a few ways:

interface IXlsxData {
  [index: number]: { 
    financialGoal: number;
    idSeller: number;
    idSupplier: number;
    positivationGoal: number;
    qtVenda: number;
  };
}

and this:

interface IXlsxProps extends Array<IXlsxData> {};
const [files, setFiles] = useState<IXlsxProps>([]);

This definition of IXlsxData is fine:

// you dont have to include ALL the existing fields here
// just the ones you're going to use later in the code
interface IXlsxData {
  financialGoal: number;
  idSeller: number;
  idSupplier: number;
  positivationGoal: number;
  qtVenda: number;
};

But if you're expecting an array of those values (as I can guess by the first log) you have to annotate useState like this:

const [files, setFiles] = useState<IXlsxData[]>([]);

Now to the real problem. The problem is XLSX.utils.sheet_to_json(ws) returns a value with type unknown[] but your setFiles is expecting a value with type IXlsxData[] . And now you have several options to ensure the compiler the result returned from sheet_to_json is of the desired type:

function isIXlsxData(data: unknown): data is IXlsxData {
  return (data != null)
      && (typeof data === 'object')
      && (typeof (data as IXlsxData).financialGoal === 'number')
      && (typeof (data as IXlsxData).idSeller === 'number')
      && (typeof (data as IXlsxData).idSupplier === 'number')
      && (typeof (data as IXlsxData).positivationGoal === 'number')
      && (typeof (data as IXlsxData).qtVenda === 'number');
}

function isArrayOfIXlsxData(data: unknown): data is IXlsxData[] {
  if (!Array.isArray(data)) return false;
  
  return data.every(isIXlsxData);
}

const dataToJson = XLSX.utils.sheet_to_json(ws);
if (isArrayOfIXlsxData(dataToJson)) {
  setFiles(dataToJson);
} else {
  // handle error
}

While type guard gives you a simple answer yes or no to the question: whether that unknown[] type is in fact your IXlsxData[] . Assertion function may give you more information on what exactly went wrong:

function assertIXlsxData(data: unknown): asserts data is IXlsxData {
  if (data == null) {
    throw new Error('IXlsxData assert failed: value is null');
  }
  if (typeof data !== 'object')
    throw new Error('IXlsxData assert failed: value is not an object');

  const errors: string[] = [];
  (['financialGoal', 
    'idSeller', 
    'idSupplier', 
    'positivationGoal', 
    'qtVenda',
  ] as const).forEach((prop) => {
      if (typeof (data as IXlsxData)[prop] !== 'number') {
        errors.push(`property ${prop} must be a number`);
      }
  })

  if (errors.length > 0) 
    throw new Error(`IXlsxData assert failed: ${errors.join(', ')}`)
}

function assertArrayOfIXlsxData(data: unknown): asserts data is IXlsxData[] {
  if (!Array.isArray(data)) 
    throw new Error('IXlsxData[] assert failed. Data is not an array');
  
  data.forEach(assertIXlsxData);
}

const dataToJson = XLSX.utils.sheet_to_json(ws);
try {
  assertArrayOfIXlsxData(dataToJson);
  setFiles(dataToJson);
} catch (ex) {
  // handle error
}
  • I KNOW BETTER way: type assertion . You may take responsibility and if you're absolutely sure nothing else but IXlsxData[] ever comes from this sheet_to_json(ws) call just force the compiler to believe you:
const dataToJson = XLSX.utils.sheet_to_json(ws) as IXlsxData[];
setFiles(dataToJson);

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM